#!/usr/bin/python3
# Generování všech posloupností 0 a 1 délky n, ve kterých je právě k jedniček
# Posloupnosti vypisujeme v lexikografickém pořadí

### Rekurzivní řešení: generujeme všechny posloupnosti délky s prefixem p,
### do kterých potřebujeme přidat n cifer, z nichž k jsou jedničky.

def genk1(n, k, p=[]):
    if n == 0:
        print("".join(map(str, p)))
    else:
        # 0 můžeme přidat, pokud zbude dost pozic na to, aby se do nich
        # vešly všechny zbývající 1.
        if k < n:
            genk1(n-1, k, p + [0])

        # 1 můžeme přidat, pokud jsme ještě všechny nespotřebovali.
        if k > 0:
            genk1(n-1, k-1, p + [1])

### A opět můžeme podobným způsobem počítat, kolik takových posloupností existuje:

def countk1(n, k):
    if n == 0:
        return 1
    else:
        result = 0
        if k < n:
            result += countk1(n-1, k)
        if k > 0:
            result += countk1(n-1, k-1)
        return result

### Počítací funkce je ovšem pomalá: výsledek je kombinační číslo (poznáváte
### v naší funkci součtový vzorec z Pascalova trojúhelníku?) a nasčítáme ho
### postupně z jedniček. Přitom "n nad n/2" je řádově 2^n / √n. Stejně jako
### u příkladu s Fibonacciho čísly si můžeme mezivýsledky pamatovat a tím
### výpočet zrychlit na O(n^2). Mimochodem, uměli byste to rychleji, aniž
### byste potřebovali mezivýsledky řádově větší než výsledek?

mem = {}        # Slovník, kde klíče jsou tuply (n,k)

def countk1b(n, k):
    if (n,k) not in mem:
        if n == 0:
            result = 1
        else:
            result = 0
            if k < n:
                result += countk1b(n-1, k)
            if k > 0:
                result += countk1b(n-1, k-1)
        mem[(n,k)] = result
    return mem[(n,k)]