Listenin permutasyonlarini siralama

swaputil = lambda mutable, i, j, k, l: mutable.__setitem__(i, l) or mutable.__setitem__(j, k)
swap = lambda mutable, i, j: swaputil(mutable, i , j, mutable[i], mutable[j])


perutil = lambda iterable, lenght, l, i = 0: [(swap(iterable, i, j) or perutil(iterable, lenght, l, i + 1) or swap(iterable, i, j)) for j in range(i, lenght)] and 0 \
          if i < lenght \
          else l.append(tuple(iterable)) # sadece itertools.permutations ile aynı olması için tuple ekliyoruz

per = lambda iterable, l: perutil(list(iterable), len(iterable), l) or l
permutations = lambda iterable: per(iterable, [])


if __name__ == "__main__":
    import itertools
    assert sorted(permutations([]))            == sorted(itertools.permutations([]))
    assert sorted(permutations([1,2]))         == sorted(itertools.permutations([1,2]))
    assert sorted(permutations(['x','y','z'])) == sorted(itertools.permutations(['x','y','z']))
    assert sorted(permutations("ABCD"))        == sorted(itertools.permutations("ABCD"))
    assert sorted(permutations([1,2,3,4,5]))   == sorted(itertools.permutations([1,2,3,4,5]))`

Yukarda per(“ABCD”) fonksiyonunun çıktı uzunluğu argüman uzunluğunun faktoriali degil mı?

permütastonlarda P(n,n) =n! dir.

2 Beğeni

Puf noktasi su: Ozyinelemeli fonksiyonlar girdiye gore iki turlu davraniyor: Base case (girdiye gore direkt cikti) ve recursive case (ozyinelemenin dondurdugu deger uzerinden cikti).

Faktoryel yazdiniz mi mesela? Recursion icin ilk verilen ornektir ama atlanabiliyor. (Base case n=0, recursive case n>0.)

Ikinci ornek icin bir sayi listesinin toplamini alan bir fonksiyon onerebilirim.
Veya carpimini, veya minimumunu, veya maksimumunu. (Monoid uzerinden fold’mus aslinda bunlar, ama bunu recursive fonksiyon yazabilmeye basladiktan 15 sene sonra ogrendim :).)

Denemeniz lazim. Dusunce adimlarinizi burada paylasirsaniz elimden geldigince yardim etmeye calisirim, ve eminim bu amacta yalniz kalmam.

şunu söyleyebilirim ki aşağıdaki linkteki örneklerin bir çoğunu kendim yazdım yazamadıklarımıda sonradan okuyunca anladım
fibonanci faktöriyel max min bunlar kolay ve rahatlıkla öngerebildiklerim bunlarda sıkıntı yaşamadım


https://nihil.ceng.metu.edu.tr/opc/pcourse/4/

kendime göre orta seviye olarak şu örneği(PASCAL ÜÇGENİNİN N.Cİ SATIRINI VERİR)

    if n == 1:
        return [1]
    else:
        SAT = [1]
        ON_SAT = pascal(n-1)
        for i in range(len(ON_SAT)-1):
            SAT.append(ON_SAT[i] + ON_SAT[i+1])
        SAT += [1]
    return SAT
for i in range(1,11):
    print(pascal(i))
ORTA ÜSTÜ OLARAK  (ALT KÜMELERİ VERİR)
def eleman_ekleyici(eleman, liste_listesi): # liste icindeki her listeye elemani ekler

    if liste_listesi == []:

        return []

    else:

        return [liste_listesi[0] + [eleman]] + eleman_ekleyici(eleman, liste_listesi[1:])





def altKumeler(liste):

    if len(liste) == 0:

        return [[]]

    else:

        return altKumeler(liste[:-1]) + eleman_ekleyici(liste[-1], altKumeler(liste[:-1]))

# ornek

# print(sorted(altKumeler(["a","b","c"])))

# >>> [[], ['a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'c'], ['b'], ['b', 'c'], ['c']]
ZOR ÖRNEK OLARAK DA BU ÖRNEĞİ VEREBİLİRİM ( PARÇALANIŞ SAYISI)
def findCombinationsUtil(arr, index, num,reducedNum):
    if (reducedNum < 0):
        return;
    if (reducedNum == 0):

        for i in range(index):
            print(arr[i], end=" ");
        print("");
        return;
    if index == 0 :
        prev=1
    else :
        prev=arr[index - 1];
    for k in range(prev, num + 1):
        arr[index] = k;
        findCombinationsUtil(arr, index + 1, num,
                             reducedNum - k);

def findCombinations(n):
    arr = [0] * n;
    findCombinationsUtil(arr, 0, n, n);
findCombinations(20)

Cok ilginc olmus! Imperatif kodun loop yerine recursion kullanilan hali, yanlis okumadiysam? :slight_smile:

Aradaki o or'lar side effect’li statement’lari birlestirmek icin mi? Alt alta da yazabilirdik:

def perutil(iterable, lenght, l, i = 0):
    if i < lenght:
        for j in range(i, lenght):
            swap(iterable, i, j)
            perutil(iterable, lenght, l, i + 1)
            swap(iterable, i, j)
    else:
        l.append(tuple(iterable))

Mesela bunu basitlestirelim:

En karmasik yer for loop’u. (Okuyarak anlamadim, calistirip bakmam gerekti ne yaptigina. + operatoru Python’da bir suru farkli is yapiyor cunku.)

i ve range kullanmamizin sebebi indise ve bir araliga ihtiyacimiz olmasi mi? Hayir, bunlar yapmak istedigimizi tane tane ifade etme aliskanligindan geliyor. Aslinda istedigimiz ardisik ciftler. itertools’a bakiyoruz:

import itertools

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)

Kodda:

def pascal(n):
    if n == 1:
        return [1]
    else:
        SAT = [1]
        ON_SAT = pascal(n-1)
        for a, b in pairwise(ON_SAT):
            SAT.append(a + b)
        SAT += [1]
    return SAT

ON_SAT’a da ihtiyac kalmadi:

def pascal(n):
    if n == 1:
        return [1]
    else:
        SAT = [1]
        for a, b in pairwise(pascal(n-1)):
            SAT.append(a + b)
        SAT += [1]
    return SAT

Peki SAT’in hakikaten hayatina [1] olarak baslayip, yavas yavas buyumesine ihtiyacimiz var mi? Hayir, basinda ve sonunda 1 olan ve arada ciftlerin toplami olan bir liste istiyoruz:

def pascal(n):
    if n == 1:
        return [1]
    else:
        SAT = [1] + [a+b for a, b in pairwise(pascal(n-1))] + [1]
    return SAT

Degiskene de ihtiyac yok:

def pascal(n):
    if n == 1:
        return [1]
    else:
        return [1] + [a+b for a, b in pairwise(pascal(n-1))] + [1]

Birden fazla return’e de:

def pascal(n):
    return ([1]) if (n == 1) else ([1] + [a+b for a, b in pairwise(pascal(n-1))] + [1])

Sonra lambda’lastirmak istersek:

pascal = lambda n: ([1]) if (n == 1) else ([1] + [a+b for a, b in pairwise(pascal(n-1))] + [1])
2 Beğeni

Bunun sebebi bu fonksiyonlarin taniminin zaten recursive verilmesi mi?
Yoo, min/max pek de oyle sayilmaz.

Onemli olan aradaki adimi bulabilmek. Mesela P(n,n) = n! dendi ya, bunun sebebi nedir? Neden P(3,3) ile P(4,4) arasinda 4 kat buyuyor liste? Fiziksel olarak bakarsak:

[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

Bunlara 4’ler nasil ekleniyor?

Evet :smile: lambda kullanmaya çalışınca böyle oluyor. Tabii algoritma ile alakalı.

Elbette, normalde öyle yapmak lazım zaten.

Anlatımınız gayet sade ve anlaşılır olmuş emeğinize sağlık böyle basamak basamak olunca anladım :slight_smile:
Ama sizin tek satırlık kodlar beni benden alıyor :smiley:

Evet, ben de side effect’li kodun imperatif stilde yazilmasi taraftariyim.

Hatta sozkonusu dil Python ise her halukarda birden fazla statement/expression/satir kullanmak lazim, yoksa okunmaz oluyor :slight_smile:

Fonksiyonel programlamanin yan etkisi. Her program tek bir expression’a indirgenebiliyor.

Ama okunamaz olduklarinin farkindayim, evet :confused:

Ha, bu arada:

per = lambda es: [()] if len(es) == 0 else [(es[i],) + sub for i in range(len(es)) for sub in per(es[:i] + es[i+1:])]
2 Beğeni

aslında her defasında bir önceki permütasyonların hepsinin aralarına serpiyoruz son elemanı ,
3 elemanlı bir kümede 3+1 dört yere yerleşebiliyor ondan 4 le çarpıyoruz

1 Beğeni

Tanımı öyle, siz tam olarak ne demek istediniz ki?

P(x, y) = x! / (x - y)!

p(n-1) mi dendi? Bir recursion mi duyuyorum burada? :slight_smile:

Yani koda cevirirsek p(n) = aralarina_serp(p(n-1), n) mi oluyor? Hmm

2 Beğeni

Hatta aralara serpme islemini e ⊙ ps = “e elemanini ps permutasyonlarinin aralarina serp” operatoru olarak tanimlarsak,

p(n) = n ⊙ p(n-1)

de diyebilir miyiz? Bu bi yerden tanidik sanki.

1 Beğeni

Tamam anladım, ama matematikte bu şekilde düşünülmüyor.

aslında özyinelemeyi kurmuş oluyoruz bir tek aradaki ikili işlemi doğru tanımlamak kalıyo geriye
anladım teşekkürler

Zaten amac matematikce dusunmeye alismis birinin programciliktaki recursion’i daha iyi anlamasini saglamak :slight_smile:

Iyi yem oldu bence =)

2 Beğeni