Diskuze: Funkce v Pythonu. Musí něco vrátit.


DarkCoder:30.8.2023 13:42
Pokud je použito return bez ničeho, sloučí to pro předčasné ukončení funkce bez vrácení hodnoty. Použití return v tomto kontextu není vhodné, neboť jakmile je proveden alespoň jeden prohoz, bude proměnná nastavena na True a tato hodnota bude platná po zbytek provádění funkce. Testy na swapped pak akorát zpomalují algoritmus.
Zde je jiná ukázka použití return bez přidané hodnoty:
def check_and_print_string(input_string):
if input_string == "Hello":
return
else:
print("Zpráva není Hello")
# Volání funkce s různými argumenty
check_and_print_string("Hello") # Funkce skončí pomocí return
check_and_print_string("Hi") # Vypíše "Zpráva není Hello"
+20 Zkušeností
+2,50 Kč

DarkCoder:30.8.2023 14:08
Něco jiného by pak bylo, pokud by se proměnná swapped reinicializovala mezi oběma cykly. To by zefektivnilo algoritmus tím, že by zbytečně neproběhly zbylé iterace pokud by již pole bylo seřazeno.
Vrátím-li se zpět k return:
Pokud funkce nevrací hodnotu, může se použít return bez přidané hodnoty
nebo nic. Return se obvykle používá k předčasnému ukončení funkce. Pokud
funkce vrací hodnotu, pak se použije return s hodnotou, která se má vrátit.
Funkce může vracet hodnotu skrz parametr funkce, pak je možné vynechat
return s hodnotou pokud měníme stejný argument nebo ji lze poslat skrz jiný
parametr a rovněž můžeme return s hodnotou vynechat. Hondotu lze vrátit i
oběma způsoby současně.
Jasně, děkuji!
S tim swapped je to tak, ze se pokousi ukoncit algoritmus, pokud nebylo swapovano. To se projevi pri vyssim poctu porovnani, kdy nastane to, ze stred byva serazen uz nekolik kroku dopredu.
Projevi se to treba uz pri 8 cislech, jako je treba videt u Shaker Sortu
https://peter.mlich.cz/…/sorting.htm#…
Jak tam mam nad kodem takovy divny klikaty pas, tak to konci cisly pod sebou
3-4, 2-3. Pak by melo nasledovat znovu 3-4, ale k zadnemu swapnuti nedoslo,
cyklus je ukoncen. To teoreticky muze nastat uz nekolik kroku drive a je
snadnejsi porovnat 1 bool nez a,b, ktere muzou byt dlouhe textove retezce.
Delal jsem treba zajimavy experiment se select-sortem, kdyz jsem si pamatoval posledni vymenu, cili, tam bude druhe nejvetsi nebo stejne cislo a dalsi cyklus jsem koncil touto pozici. zisk byl v prumeru asi 17% (tusim, ze jsem mel asi 1024 cisel). Zkusil jsem si pamatovat vsechny operace a slo asi o 21% (coz je neprakticke, protoze tech muze byt v nejhorsim pripade i delka_pole-1). U Bubble sortu a Shaker sortu jsem s tim neexperimentoval. Odhadoval bych to spis na max 3%, ale i to muze byt dobre.
Tak hluboce to nechápu. No, je toho hodně, o co se musíme snažit!
DarkCoder:31.8.2023 14:13
To už je algoritmizace. K tomu ale potřebuješ znát možnosti jazyka ve kterém chceš tvořit. Pokud znáš operace s proměnnými, operátory, pole, funkce a řídící příkazy, můžeš si zkusit naimplementovat např. Selection sort o kterém tu byla řeč.
Jo, Selectionn Sort a Bubble Sort jsem probral. Všechno je jasné, i když obtížné. Ale ohledně insertion Sort podívej se prosím na kód:
def insertionSort(zadanePole): # uživatel zadává čísla: 95, 2, -5, 7, 0
i = 0
klic = 0
j = 0
for i in range(1, len(zadanePole), 1): # 1 2 3 4
klic = zadanePole[i] # 2 -5 7 0
j = i - 1 # 0 1 2 3
while (j >= 0 and zadanePole[j] > klic ):
zadanePole[j + 1] = zadanePole[j]
j = j - 1
zadanePole[j + 1] = klic
cisla = input("Zadej čísla pro seřazení (oddělená čárkou): ")
vstupniPole = [int(n) for n in cisla.split(",")]
insertionSort(vstupniPole)
print("Seřazená čísla:")
print(*vstupniPole, sep=', ')
Poraď prosím co tady znamená j = j - 1
? Pro co to je tady?
Jakou hraje roli?
DarkCoder:1.9.2023 12:05
U Insertsortu seřazuješ a postupně zvětšuješ část pole nalevo od řazeného prvku dokud není celé pole seřazené. Pro řazený prvek je třeba najít správnou pozici v seřazené části pole. Procházíš prvky části seřazeného pole odzadu a porovnáváš se řazeným prvkem. To děláš tak dlouho dokud je řazený prvek větší a zároveň nejsi na začátku. Aktualizace pozice prvku při směru odzadu dělá právě výraz j = j - 1.
Insert. Zkus tu moji stranku, treba to z toho pochopis.
57226 - na zacatku mas nahodne cisla
5 -> [5] vemes dalsi a zaradis ho do out-pole, porovnavas od zacatku out-pole (out-pole mas na zacatku prazdne)
7 -> [5, 7] - pak vemes dalsi a zaradis ho do out-pole, porovnavas od zacatku out-pole, cili 5 a 7
2 -> [2, 5, 7] - porovnavas 2 a 5, 2 je mensi nebo rovno, jde tedy pred 5, uz neporovnavas
No, a abys mohl zaradit cislo jinam nez na konec, tak je nutne cast pole posunout. To se nejjednoduseji dela od konce kopirovanim pole[len+1] = pole[len], pole[len+0] = pole[len-1], pole[len-1] = pole[len-2]...
[5, 7]
[_, _, 7] pozn: _ podtrzitko, jakoze s temi cisly nic nedelam
[_, 5, _] a na prvni pozici ted muzu dat tu dvojku
[2, 5, 7]
A kdyz potrebujes dat neco doprostred, treba pred d
[a, b, c, d, e, f, g]
[a, b, c, | d, e, f, g] - od 'g' po 'd' to posunes o +1 doprava
[a, b, c, d, d, e, f, g] - 'd' a 'd' je tam duplicicne a prvni 'd' nahradis novym cislem, ktere ma byt pred 'd'
[a, b, c, new h, d, e, f, g]
To dela ten druhy cyklus v insert sortu.
A zajimavejsi je, zmenit ten prvni algoritmus, hledani kam zaradit na puleni
intervalu. Vis, ze druhe pole je uz serazene. Takze, kdy nove cislo bude vetsi
nez cislo v polovine serazeneho pole, tak proti beznemu insertsortu preskocis
polovinu pole uz v prvni kroku. Takto postupujes dal.
Pokud je pole dlouhe 1.000.000 prvku, tak novy prvek zaradis do 7
porovnani. Coz je super, ne?
Klasicky Insert-top-sort, pokud by musel prvek zaradit na konec potrebuje
1.000.000 porovnani. To je sakra rozdil.
Problematicka cast je vsak stale nutnost posouvat pokazde cele pole
Cili, ani insert sort bych nedoporucoval. Ale pozor, insert-middle-sort ma uplne
nejmensi pocet operaci porovnani ze vsech. Az na tou pomalou ast je to skvely
algoritmus.
Většina standardních implementací Insertion Sortu používá pouze jedno pole pro třídění, protože to je jednodušší a efektivnější z hlediska paměti a času.
Insertion Sort s jedním polem pracuje přímo s původním polem a nepotřebuje další paměťové alokace pro druhé pole. Algoritmus používá konstantní paměťovou režii, což je výhodné, když máme omezenou paměť.
Implementace Insertion Sortu s jedním polem je jednodušší a přehlednější, protože nepotřebujeme sledovat dvě různá pole a kopírovat prvky mezi nimi.
Insertion Sort je obecně vhodný pro třídění malých polí. V těchto případech je přidaná složitost použití druhého pole zpravidla nepatrná, ale může mít vliv na časovou složitost.
Avšak Insertion Sort je jedním z nejpomalejších algoritmů pro třídění v průměrném a nejhorším případě, ať už použijeme jedno nebo dvě pole. Pro třídění větších polí nebo pro situace, kdy se vyžaduje vysoký výkon, bude lepší použit jiný algoritmus, jako třeba Quick Sort nebo Merge Sort, které mají lepší časovou složitost. Použití binárního vyhledávání oproti lineárnímu docílíme rychlejšího nalezení místa pro vložení prvku.
Třeba poslední otázka.
V tomto kódu:
def insertionSort(zadanePole): # uživatel zadává čísla: 95, 2, -5, 7, 0
i = 0
klic = 0
j = 0
for i in range(1, len(zadanePole), 1): # 1 2 3 4
klic = zadanePole[i] # 2 -5 7 0
j = i - 1 # 0 1 2 3
while (j >= 0 and zadanePole[j] > klic ):
zadanePole[j + 1] = zadanePole[j]
j = j - 1
zadanePole[j + 1] = klic
cisla = input("Zadej čísla pro seřazení (oddělená čárkou): ")
vstupniPole = [int(n) for n in cisla.split(",")]
insertionSort(vstupniPole)
print("Seřazená čísla:")
print(*vstupniPole, sep=', ')
proměnna lkič
pišeme, že
klic = zadanePole[i] ` a proto má čísla `2 -5 7 0
.
Dále vidíme, že kód končí řídicím příkazem
zadanePole[j + 1] = klic
A tady je otázka - jak kód rozumí kam v klic
dat to
zadanePole[j + 1]
?
Vložit číslo doprava, doleva, uprostřed?
DarkCoder:4.9.2023 11:29
To je přeci prosté, klíč je proměnná která v sobě udržuje právě jednu hodnotu, nikoli seznam. Proměnná j je celé číslo, jehož hodnota závisí na iteraci která právě probíhá a později na podmínce pro vkládání klíče. zadanePole[j] představuje aktuální prvek, zadanePole[j+1] následující prvek. Výsledná hodnota v hranatých závorkách představuje index pole.
zadanePole[n] na levé straně představuje pozici v poli
zadanePole[n] na pravé straně představuje hodnotu na pozici v poli
Výraz
zadanePole[j + 1] = zadanePole[j]
tak můžeš číst jako: Do následujícího prvku přiřaď hodnotu aktuálního prvku. Významově to znamená posun prvku vpravo.
Jelikož víš že j je celé číslo, pak víš přesně, jakou hodnotu na jakou pozici v poli ukládáš.
Musis si uvedomit, ze pracujes se serazenym polem a staci narazit na prvni hodnotu, ktera nevyhovuje podmince a nebudou vyhovovat i zadne dalsi. Takze, to je ta pozice, kam musis to nove cislo zaradit.
Prvni cyklus prohazi pole
for i in range(1, len(zadanePole), 1)
klic = zadanePole[i]
01234
ECADB
i = 1, 2, 3, 4
klic = C, A, D, B --- vynechava prvni hodnotu E
... to je asi jasne
Druhy cyklus zacina predposledni hodnotou (posledni je v klic) a jde na
zacatek. Ale, musi tam byt podminka pro j >= 0
, aby neskoncil na
pozici` j=-1`.
while (j >= 0 and zadanePole[j] > klic ):
zadanePole[j + 1] = zadanePole[j]
j = j - 1
abcdefgh C fdhbvuea - serazene pole + C (nova hodnota, klic) + nahodna data
Porovnavas v serazenych datech od konce
a kdyz to nesedi, zadanePole[j] > klic, tak zkopirujes porovnavanou hodnotu na jeji pozici+1, zadanePole[j + 1] = zadanePole[j]
abcdefgh C fdhbvuea, klic = C (C mam zalohovane, muzu tam zapsat, co chci a to take delam)
abcdefgh H fdhbvuea
abcdefgG H fdhbvuea - a postupne prepisuji i vsechny ostatni, dokud plati podminka cyklu
abcdefFG H fdhbvuea
abcdeEFG H fdhbvuea
abcdDEFG H fdhbvuea - a ted prestava platit podminka `C (pole[j]) > C (klic)`
0123
j = 3, j+1 = 4 a tam chces umistit klic
abcdDEFG H fdhbvuea - cili, prepises male 'd' klicem 'C'
abcCDEFG H fdhbvuea
Snad to je aspon trochu srozumitelne. Ono se to spatne vysvetluje
A take muze byt matouci, ze porovnavas od konce. Ono je to jedno, od konce, od
zacatku, samozrejme nejlepe od stredu . Zamerne je vybrane od konce, protoze
potrebujes posouvat ty hodnoty. V pripade porovnani od stredu pouzivam dalsi
cyklus jen na to posouvani. Tady delas oboje najednou, zjistujes, kam umistit a
zrovna posunujes take pole.
A ne, pro ten muj insert do stredu plati, ze pomaly neni, pomale je jen to
posouvani pole
Coz ale umi moderni procesor urychlit predvidanim predchozi operace. Dokonce
jsem videl urychleny bubble sort, ktery byl rychlejsi nez quick-sort. A, je
dobre vedet, ze quick-sort neni stabilni. Jde napsat stabilne, ale k tomu
potrebujes dalsi pole. A kdyz mas uz 2 pole, tak je nejrychlejsi a uspornejsi
spojovani serazenych poli delky 1, 2, 4, 8, 16....
DarkCoder:4.9.2023 14:32
Existuje varianta QuicSortu tzv. Ternary QuickSort (MultiKey QuickSort), který udržuje stabilitu bez použití druhého pole.
Velmi dobře. Krok za krokem je prehlednější. Dovol se ale mi vratit k
Tvému:
U Insertsortu seřazuješ a postupně zvětšuješ část pole nalevo od řazeného prvku dokud není celé pole seřazené. Pro řazený prvek je třeba najít správnou pozici v seřazené části pole. Procházíš prvky části seřazeného pole odzadu a porovnáváš se řazeným prvkem. To děláš tak dlouho dokud je řazený prvek větší a zároveň nejsi na začátku. Aktualizace pozice prvku při směru odzadu dělá právě výraz j = j - 1.
Samotná myšlenka je jasná, ale žádám Ti, abyste znovu objasnil
následující:
1.
U Insertsortu seřazuješ a postupně zvětšuješ část pole nalevo od řazeného prvku dokud není celé pole seřazené.
Ale ve skutečnosti uvažujeme pole napravo od části, kterou začínáme
uvažovat. Zde je to v prvním cyklu číslo 95 a číslo 2.
Podivej se v kódu, prosím:
while (j >= 0 and zadanePole[j] > klic ):
zadanePole[j + 1] = zadanePole[j]
# první iterace: while (0 >= 0 and 95 > 2): číslice 95 a 2 mění místa. Tj. "zadanePole" po první iteraci vypadá takto "2, 95, -5, 7, 0 ".
#druhá iterace: while (1 >= 0 and 95 > -5):číslice 95 a -5 mění místa. Tj. "zadanePole" po druhé iteraci vypadá takto "2, -5, 95, 7, 0 "
2. Procházíš prvky části seřazeného pole odzadu
Zda to znamená, že:
- znamená to, že se pole
zadanePole[j]
předává zezadu, tedy zprava doleva?
A řidici příkaz tomu je j = j - 1
?
Jistě opakuji, co Jsi již vysvětlil, ale pro mě, jako začátečníka, je
důležité porozumět všem prvkům.
To je důvod, proč se ptám několikrát na totéž, ale z různých
úhlů.
Tys to spatne pochopil
i = 1, 2, 3, 4, - cyklus 1 pocita takto
j = 0, 10, 210, 3210 - cyklus 2 pocita pro kazde i od konce
hodnota klic se vyjme z pole z posledni pozice za serazenou casti
mame jine cisla nez ty a jsem nekde uprostred cyklu pro icko
abcdefgh C fdhbvuea - pred C jsou serazene cisla, C + cisla za nim jsou ta, ktera do te predchozi casti chces vlozit.
Tu serazenou cast tedy musis prohledat, ze predu nebo ze zadu. Tvuj cyklus ji prohledava ze zadu.
Jestli to potrebujes s cisly, tak treba
abcdefgh C fdhbvuea
13579999 5 0524386 - cisla na zacatku jsou uz serazena, ostatni jsou nahodna
012345678 9 ............. - i
i = 9, pole[i] = 5 = klic
Dobre, ted to pisu blbe ja, to cislovaji
01234567 - poradi v poli (index pole)
gbdahefc - nahodna cisla
i = 1 (j0 = i-1)
j = 0
i = 2
j = 1, 0
i = 3
j = 2, 1, 0
i = 4
j = 3, 2, 1, 0
Druhy cyklus prochazi pole od konce a zjistuje, zda je cislo stale vetsi.
kdyz neni, nasel misto, kam ho ma vlozit.
A soucasne pri tom si presunuje i cisla v poli.
DarkCoder:4.9.2023 19:31
Je třeba nad tím uvažovat celkově. Prvky vlevo od řízeného prvku jsou seřazené a tam budeš řazeny prvek vkládat. Prvky napravo včetně aktuálně řízeného prvku jsou Prvky, které je třeba zařadit. Vše je důležité.
Vytvoř si malé kartičky s čísly od -1 do 10 a polož je na zem v vzestupnem pořadí. Tato čísla představují indexy. Dále si Vytvoř kartičky s čísly od 0 do 7 a navíc dvě kartičky s nějakým stejným číslem. Na tyto dvě si napiš malými čísly jedničku. U druhych opakujicich pak číslo 2. Tyto kartičky představují hodnoty. Kartičky zamíchat a polož je pod Kartičky 0 až 9. Vytvoř si dva tokeny, i a j. I představuje index řazeneho prvku a j představuje index prvku kam se bude řazeny prvek vkládat. Dej je úplně nahoru nad Kartičky podle jejich inicializace. Vytiskni si zdrojový kód řadiciho algoritmu insertion sort a jeď příkaz po příkazu. Přesouvají si tokeny a Kartičky dle příkazů v algoritmu. Uvidíš jasně co se tam děje, jak se mění hodnoty, jak se mění indexy. Je to nejlepší způsob jak pochopit princip algoritmu.
Nebo, bych mozna pridal princip insert sort pri prohledavani zhora dolu (zleva doprava)
# uživatel zadává čísla: 95, 2, -5, 7, 0
klic = 95, out = []
out[0] = 95
klic = 2, out = [95], j = 0 # serazene pole prohledavas cyklem od min po max,
# kde min = 0 vzdy a max = len(out)
if (klic>out[j]) j = j + 1 else insert_on_pos(j) # 2>95
if (2>95) else insert_on_pos(j)
klic = -5, out = [2, 95], j = 0
if (-5>2) else insert_on_pos(j)
klic = 7, out = [-5, 2, 95], j = 0
if (7>-5) j = j + 1
if (7>2) j = j + 1
if (7>95) else insert_on_pos(j) # j = 2
klic = 7, out = [-5, 2, 95], j = 0
if (0>-5) j = j + 1
if (0>2) else insert_on_pos(j) # j = 1
Tohle je asi celkem jasne, ze prohledavas serazene pole, dokud je cislo
mensi. Pak narazis na misto, kde je vetsi a tam to nove cislo (klic) chces
umistit.
A to umisteni provedes tak, ze pravou cast posunes o 1 doprava. Posouvani se
dela od konce.
cyklus start: k = len(out) - 1
cyklus increment: k = k - 1
cyklus end: k==J
posun: out[k+1] = out[k]
za cyklem insert: out[J] = klic
Ve tvem kodu to delas podobne, ale prohledavas serazene pole od konce, abys
nasel pozici, kam cislo patri. To je asi jedno, zda od zacatku nebo od konce,
ne? A jeste nabizim tu treti moznost, prohledavat pulenim intervalu. Ale ten kod
by byl jeste slozitejsi. Ukazka viz ta ma stranka, asi 30 radku
Py nabizi jeste jinou moznost insertovani. Ta se ti mozna bude libit a zdat jednodussi. Ale vyzaduje vic pameti. Snad to napisi spravne, pac PY moc nepouzivam, uz.
pos = najdi_pozici_pro_vlozeni() # dopln si kod pro hledani pozice
out = out[0:pos-1] + klic + out[pos:len(out)]
# pole[a:b] zkopiruje pole do noveho pole od A po B
Cili, kdyz vis, na ktere misto to chces umistit, tak si zkopirujes cast pred, cast za a mezi to vlozis klic. Da se tim nahadit tato cast kodu, ale program to zpomali, nepatrne a bude potrebovat dalsi pamet pro kopirovana pole jako celky a ne bunku po bunce.
while (j >= 0 and zadanePole[j] > klic ):
zadanePole[j + 1] = zadanePole[j]
j = j - 1
zadanePole[j + 1] = klic
while (j >= 0 and zadanePole[j] > klic ):
j = j - 1
zadanePole = zadanePole[0:j] + klic + zadanePole[j:len(zadanePole)]
(ale, mozna tam nekde melo byt j-1 nebo j+1, nechce se mi ted premyslet)
DarkCoder:5.9.2023 11:02
Zde máš zevrubný popis funkčnosti algoritmu Insertion Sort:
Insertion sort má následující podobu:
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j = j - 1
arr[j + 1] = key
V insertion sortu se ve výchozím nastavení považuje první prvek pole jako seřazený. Tento prvek se nebere jako klíč!
Postupně se budou brát všechny zbylé prvky pole a vkládat se na správné místo v poli. Což je druhý až poslední prvek. To je důvod, proč cyklus for má tuto podobu:
for i in range(1, len(arr)):
Znamená to, že iterace probíhají nad druhým až posledním prvkem (index 1 = druhý prvek). A tyto prvky se postupně berou jako klíče:
key = arr[i]
Nyní je třeba najít index posledního již seřazeného prvku. To je vždy prvek první zleva od klíče:
j = i - 1
Tím máme vytvořenou část kódu pro postupné stanovení klíčů a vyhledání pro ně posledního prvku seřazeného subpole (jedná se o posloupnost již seřazených prvků).
Nyní je třeba klíče zařadit na správnou pozici subpole. Je tedy třeba porovnat velikost klíče se seřazenými prvky subpole. Index posledního seřazeného prvku jsme si zjistili vztahem j = i - 1. Porovná se hodnota prvku na indexu j s hodnotou klíče a zároveň se bere v potaz to zda index je validní.
while j >= 0 and arr[j] > key:
Dokud jsou již seřazené prvky větší než klíč, posouváme je vpravo.
arr[j + 1] = arr[j]
Na pozici vpravo stál klíč, nemusíme se bát, že přepíšeme nesprávný prvek. Klíč budeme vkládat na správné místo později.
Aby se měnil index porovnávaného prvku s klíčem a zajistilo se tak postupné procházení seřazeným subpolem, je třeba aktualizovat index. Procházíme seřazené prvky od posledního postupně k prvnímu, doku klíč je menší než porovnávaný prvek. Procházíme tedy zprava doleva, tedy je třeba získat index prvku vlevo od posledně porovnávaného. To není nic jiného než:
j = j - 1
Jakmile hodnota klíče bude větší než aktuálně porovnávaný prvek subpole, Cyklus končí a dochází k zařazení klíče do subpole, čímž se vytváří aktualizované subpole které je větší o klíč.
Když klíč bude větší než aktuálně porovnávaný prvek seřazeného subpole, vkládáme klíč do subpole. Vkládáme ho za porovnávaný prvek a to popisuje právě tento příkaz:
arr[j + 1] = key
Po vložení klíče do subpole končí iterace cyklu a dochází k nové iteraci kde se opět bere následný prvek k zařazení. A to se opakuje dokud nejsou všechny prvky k zařazení zařazeny do subpole.
Tím se vytvoří seřazené pole pomocí algoritmu Insertion Sort.
Pokud Ti i přesto cokoli není jasné, ptej se.
Alexandr Saveljev:5.9.2023 11:17
Je třeba nad tím uvažovat celkově.
- máš právdu, ale to i
je problemem pro mě.
Nechápu "celý cyklus". Ale nic, Jste a Peter - jste vše popsali jasně,
a pravděpodobně udělám to - "Vytvoř si malé kartičky s čísly od -1 do
10 a polož je na zem v vzestupnem pořadí. ... ".
Děkuji!!!
DarkCoder:5.9.2023 11:55
Všechno přijde. Analýza cizích kódů a plné porozumění algoritmům je náročná činnost. Je třeba najít si způsob jak toho co nejsnáze dosáhnout. Umět si to představit, předvídat, porozumět různým trikům, apod. Interaktivní podoba v podobě kartiček a tokenů pro porozumění algoritmům Ti jistě pomůže. Výše jsem ti popsal detailní funkčnost algoritmu Insertion Sort. Když si budeš analyzovat pomocí kartiček jednotlivé příkazy, nahlédni i do popisu k příkazům a porovnej to co jsem tam napsal s tím co se děje a co provádíš. Čím více si toho vyzkoušíš analyzovat, tím rychleji a kvalitně pochopíš mnohému dalšímu. Nemáš zač.
Tak, jde lepší a lepší, ale mám otázky:
V insertion sortu se ve výchozím nastavení považuje první prvek pole jako seřazený. Tento prvek se nebere jako klíč!
- bravo!A tyto prvky se postupně berou jako klíče: key = arr[i]
Otázka: jak Python rozumí, že postupně
, a ne celý řetězec
čísel, zadaný uživatelem?
Jde tady o cyklu "for", kde proměnna "i" bere se postupně, a jestli proměnna
"j" nemá rozsahu, ale je i - 1
,
pak se podle toho i "j" bere se jedno číslo?
3. To je vždy prvek první zleva od klíče: j = i - 1
Jak Python
rozumí, že jde o indexu proměnné i
, a ne o její hodnotě?
Nejsou zde žádné hranaté závorky, jakoby např. ` j = [i - 1]`.
Zatim dělam STOP a čekám na odpověď.
Asi jsem Tě už umučil.
DarkCoder:5.9.2023 14:32
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
...
Odpověď se nachází v druhém řádku kódu. i je iterační proměnná cyklu a v každé iteraci je ta hodnota jiná, o jedna větší. Nejprve je 1, provedou se všechny příkazy cyklu. To je iterace. V další iteraci bude i rovno 2, atd.
Dále je použito pole a přístup ke konkrétnímu prvku pole se provádí pomocí indexu. Iteracni proměnná i slouží pro průchod polem.
key = arr[i]
Výraz na pravé straně přiřazovaciho příkazu využívá indexace pole k přístupu ke konkrétnímu prvku. Na počátku je i rovno 1, Výraz arr[1] představuje druhý prvek, je to hodnota která je pak přiřazena proměnné key. Pomocí indexu můžeš přistupovat k hodnotě daného prvku pole. Jednoduše tím že indexuješ identifikátor pole.
Děkuji!
Ale dneska už nemohu psat.
Do zítřka!
DarkCoder:5.9.2023 14:42
Takže pomatovat:
Index je ordinarni číslo, pomocí kterého přistupujeme k hodnotě prvku pole.
Máš-li Výraz
arr[i]
Pokud se vyskytuje na levé straně prirazovaciho příkazu, měníš hodnotu prvku pole na indexu i.
Pokud se vyskytuje na pravé straně prirazovaciho příkazu, používáš hodnotu která je uložena v poli na i-tem indexu.
V cyklech se využívají iteracni proměnné pro přístup k hodnotě prvku pole na indexu hodnoty iterační proměnné.
Tak, tak, pokud te programovani zajima, tak dojde i na to, ze cizi kod
pochopis. Pochopit muj popis je horsi
(pozor, nazvy algoritmu jsou pojmenovane, jak jsem chtel ja, nikoliv nejaka
literatura)
Ale, muzu ti ukazat jiny algoritmus, ktery snadneji pochopis. Ale kod ma sileny,
pac to jinak ani nejde.
https://peter.mlich.cz/…/sorting.htm#…
Jestli jsi nekdy soutezil, tak je to klasicky herni pavouk. Dva zapasi proti
sobe, vitez postupuje do dalsiho kola.
A dalsi krok je odstraneni viteze a prepocitani casti pavouka, abys zjistil
druheho nejlepsiho (v pripade sortovani, druhe nejmensi cislo).
A nebo, snadny na pochopeni by mohl byt i tento:
https://peter.mlich.cz/…/sorting.htm#…
Jedna se o spojovani serazenych seznamu.
Mas hromadky, ktere jsou serazene (protoze je tam jeden prvek, v kazde, tak jsou
serazene).
Spojis je na hromadky po dvou tak, ze porovnavas v kazde prvni cisla a to mensi
prelozis na treti hromadku na zacatek. Opakujes, dokud nevycerpas jednu
hromadku. Ze zbyle hromadky proste cisla presunes do te treti.
Treti hromadka je pak opet serazena, jeji velikost je dvojnasobna, spojujes ji s
dalsi takovou. Atd
1) = === ==== ==
2)
=
===
==
====
3)
=
==
===
====
Opet, kod neni uplne snadne napsat. Pokud bys chtel pouzit jen jedno pole,
tak musis neustale presouvat jako u insert-sortu.
Pokud pouzijes treti hromadku (cili vice pameti, dve pole), tak je pocet presunu
do 10 mezi polem1 a 2.
Nebo, na to muzes jit jinak, zjistit si, ktera casti jsou uz serazene a ty
spojuj.
https://peter.mlich.cz/…/sorting.htm#…
7
2
3
5
6
0
1
4
serazene casti jsou
7
2
3
5
6
0
1
4
A pak spojujes serazene casti, jako v predchozim pripade.
DarkCoder:5.9.2023 20:05
Opakovaný Merge Sort nad vyhraněnými a seřazenými subpoly.. 🙂 Celkem užitečná věc.
Az na to, ze Merge sort pracuje jinak. Cast s mergovanim ma take, ale predtim
dela jeste nejake preusporadavani. Ktere v mem pripade uz neni treba delat.
A, uz jsem to psal jinde, vetsina tech sortu, kterymi se zabyvam vic je stejne
rychla a vetsinou lepsi nez quick sort. Hlavni prednosti je stabilita, kterou
potrebuje treba bzip.
Otázka po kódu:
def insertion_sort(arr): # 95, -4, 0, 3, -9
for i in range(1, len(arr)):
key = arr[i]
#print(key, end=" ") # -4 0 3 -9
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j = j - 1
arr[j + 1] = key
Tady key
- to je dočasné úložiště
kam
prvky se postupně berou jako klíče
.
Souhlasím.
Ale když jsem udělal print() tohoto key
, pro zadaná čísla
95, -4, 0, 3, -9
mám vytusknuté -4 0 3 -9
.
Otázka: proč? Mělo by tam být jen jedno číslo, ne?
Nebo je tato otázka nevhodná?
DarkCoder:7.9.2023 10:24
Ano, proměnná key slouží pro uložení řazeného čísla. To je důležité, neboť jeho pozice může být přepsána poslední seřazenou hodnotou. Když si vypisuješ key, tak ti to vypíše počet hodnot v závislosti na počtu iterací. Ty při seřazení 5 hodnot proběhnou 4, proto máš na obrazovce vytištěné 4 hodnoty.
Jasně, děkuji!
# arr = [95, -4, 0, 3, -9]
# len(arr) 5
for i in range(1, len(arr)):
key = arr[i]
i = 1 ---> key = arr[1] = -4
i = 2 ---> key = arr[2] = 0
i = 3 ---> key = arr[3] = 3
i = 4 ---> key = arr[4] = -9
Peter Mlich - ano, této části kódu už rozumím.
Co dosud NErozumím:
j = j - 1
arr[j + 1] = key
Můj problém je, že nedokážu přesně vyjádřit to, čemu
nerozumím.
Ale právě na tom pracuji, prošel jsem celý kód a formulace přijde
později.
DarkCoder:8.9.2023 12:38
Vytvořil jsi si už kartičky a tokeny jak jsem Ti radil a analyzoval
pomocí nich algoritmus interaktivním způsobem?
Pokud ještě ne, udělej to. Vše na co se zde ptáš je tam vidět a na vše
dostaneš odpověď.
Než vstupuješ do následujícího cyklu:
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j = j - 1
udržuje j index posledního seřazeného prvku subpole. Jednoduše index prvku nalevo od zařazovaného čísla.
Následuje cyklus pro nalezení místa kam zařazovaný prvek vložit. Z podmínky cyklu je vidět, že se porovnává hodnota posledního prvku subpole s hodnotou zařazovaného prvku. Pokud je větší, přesouváš hodnotu posledního seřazeného prvku o jedno doprava, což je místo zařazovaného prvku, tedy klíče.
Abys prošel celé subpole seřazených prvků, musíš aktualizovat prvek, který budeš porovnávat s klíčem. Nejprve si testoval poslední a abys jej prošel celé, musíš se dostat až k prvnímu. Musíš tedy měnit hodnotu j tak aby si dokázal přistupovat postupně k prvnímu prvku odzadu. Další prvek na testování je tedy předposlední prvek subpole a jaký je jeho index? O jedna menší než index posledního prvku subpole. Proto posun v subpoli je dán příkazem:
j = j - 1
To provádíš spolu s posunem tak dlouho, dokud platí podmínka cyklu. Když cyklus skončí z důvodu neplatné podmínky, máš v j hodnotu která není indexem prvku kam máš vložit hodnotu zařazovaného prvku. Je to hodnota o 1 menší protože aktualizace indexu následovala za posunem. Když jsem Ti psal o kartičkách, uvedl jsem rozsah od -1 do 10. Ta -1 má smysl protože j se na tuto hodnotu může dostat, pokud by byl zařazovaný prvek v tu chvíli nejmenší ze všech aktuálně seřazených prvků.
No a protože j po skončení cyklu udržuje index prvku nalevo o pozice kam máš zařadit zařazovaný prvek, je třeba index zvednout o jedna, aby si se dostal na správnou pozici. To je dáno příkazem:
arr[j + 1] = key
Pak probíhají další iterace dokud nejsou všechny zařazované prvky umístěny na svých místech.
Aha, asi mam tuseni. Sice jsem to uz psal, ale neva.
Totiz, delal jsi jiny jazyk? Python ma takovou jednu osklivou nepeknou vec,
kterou vyvojari prezentuji jako bonus. PY zrusil ohraniceni kodu pomoci
begin-end znacek a presel na automaticke znaceni podle odsazeni zleva. Ono je to
dost zasadni pro pochopeni kodu.
def insertion_sort(arr): # 95, -4, 0, 3, -9
# function open
for i in range(1, len(arr)):
# cycle-1 open
key = arr[i]
#print(key, end=" ") # -4 0 3 -9
j = i - 1
while j >= 0 and arr[j] > key:
# cycle-2 open
arr[j + 1] = arr[j]
j = j - 1
# cycle-2 close
arr[j + 1] = key
# cycle-1 close
# function close
# tyto dva radky se provadi jen v tom cyklu
arr[j + 1] = arr[j]
j = j - 1
# cyklus skonci, kdyz nejsou splnene podminky
while j >= 0 and arr[j] > key
# A) prvni podminka j >= 0 (do J jsi na zacatku dal hodnotu J=I-1, cili J muze byt i nula, ale vetsinou bude vetsi nez 1)
# uvnitr cyklu J snizujes pres J = J -1 # do J dej puvodni hodnotu J a uber jednicku.
# Kdyz bylo J=5, treba, tak nove bude 5-1, cili 4
# a v dalsim kroku cyklu se opet provede j = j - 1 , ze 4 bude 3...
# a cyklus pojede, dokud bude platit j>=0
# B) druha podminka arr[j] > key - si rikal, ze chapes
# C) vztah mezi podminkami je: A and B, musi platit tedy obe soucasne,
# kdyz prestane platit jedna z nich, cyklus se prerusi
# Kdyz se cyklus prerusi, v J zustane posledni hodnota. To muze byt treba 3.
# to je pozice zhruba pozice, kam patri cislo v promene klic (key)
arr[j + 1] = key
Hele, tak pouzij ten bubble sort, pokud kodu rozumis. Pokud to delas jen pro ucitele. V normalnim programu bys tohle vubec neresil, PY ma sort jako vestavenou funkci.Takhle se to spis pouziva pro ucebni ucely.
Zkusim to jeste schematicky, jina sada cisel, 88105582
( znak '=' pouzivam jako nahradu za prazdne policko )
88105582
8=105582 # i=1 klic=pole[i]=8 (druhe cislo v poradi, hodnotu klice z pole jakoby odstranis)
# a cyklus-2 ho pak porovnava od zadu se vsemi predchozimi,
# dokud se nezastavi na jedne nebo druhe podmince
# po skonceni cyklu-2 zustane v J pozice posledni porovnavaci operace, ktera cyklus ukoncila
# (a nebo to skoncilo kvuli prvni podmice j>=0)
8=105582
8
88105582 # vysledek po zarazeni, po skonceni cyklu-2, a dal pokracujeme cyklem-1
88105582
88=05582 # - i=2 klic=pole[i]=1 (treti cislo v poradi), opet, porovnas se vsemi predchozimi, od konce
=1 # 1 a 8 podminkou arr[j] > key, key je mensi, cyklus porovnava s dalsim predchozim cislem)
1 # 1 a 8, opet by cyklus pokracovat, ale selze druha podminka,
# protoze nove J=-1 a nevyhovuje podmince J>=0),
# zaradim klic pred 1 (skoncilo to podminkou j>=0)
18805582 # vysledek
188=5582 # klic = 0
==8 # porovnam klic s 8
=8 # porovnam klic s dalsi 8
1 # porovnam klic s dalsi 1, konec, zaradim klic pred 1 (skoncilo to podminkou j>=0)
01885582 # vysledek
0188=582 # klic = 5
===8 # porovnam klic s 8
==8 # porovnam klic s 8
=1 # porovnam klic s 1, konec, klic je >1, zaradim klic za 1
01588582 # vysledek
01588=82 # klic=5
====8 # porovnam klic s 8
===8 # porovnam klic s 8
==5 # porovnam klic s 5
=1 # porovnam klic s 1, konec, klic je >1, zaradim klic za 1
01558882 # vysledek
015588=2 # klic=8
=====8 # porovnam klic s 8, konec, zaradim klic za 8
01558882 # vysledek
0155888= # klic=2
======8 # porovnam klic s 8
=====8 # porovnam klic s 8
====8 # porovnam klic s 8
===8 # porovnam klic s 5
==5 # porovnam klic s 5
=1 # porovnam klic s 1, konec, klic je >1, zaradim klic za 1
A tim, ze dobre sikovnym zpusobem pocitas J a soucasne pri kazdem porovnani presouvas hodnoty, tak otazku zarazeni PRED a ZA nemusis vubec resit. V J+1 bude spravna pozice odpovidajici situaci zarazeni PRED a nebo ZA.
Ohledně
V normalnim programu bys tohle vubec neresil, PY ma sort jako vestavenou funkci.
- jakou
vestavenou funkci máš na mysli?
Insertion sort
, nebo sorted()?
Pavel:11.9.2023 16:25
Sort a sorted jsou jenom názvy funkcí, obě by měly být implementací Timsortu (https://en.wikipedia.org/wiki/Timsort))
Ne, otázkou je, má Python jako vestavené funkce Insertion sort, Bubble nebo Selection sort?
Pavel:11.9.2023 19:17
Vestavěné funkce používají Timsort, pokud chceš v Pythonu implementaci jiné metody, tak si ji napíšeš sám, nebo se podíváš sem: https://pypi.org/…ect/sorting/
Peter Mlich:12.9.2023 8:28
Programovaci jazyk ma vestavenou vetsinou jedinou funkci. K cemu ty ostatni?
Nepotrebuje delat nejake statistiky. Proc zatezovat vyvojare a disky
uzivatelu?
Nekde v dokumentaci byva zmineno, co to je, pripadne kod. Nevim, co je v PY, ale
v soucasnosti nejlepsi je Tim-sort, ktery je vytvoreny jako propojeni algoritmu
insert-sort (tusim pro 7 prvku) + list-merging. (kod Timu zapsany pomoci
javascriptu ma treba 20k)
Jestli chces delat testy, neni problem si najit nejakou stranku, kde staci
pridat jen algoritmy a py kod asi taky najdes.
Ja si treba takove testy delal v JS, protoze JS dneska funguje na kazdem pc a
webu.
https://peter.mlich.cz/…g4-pokus.htm
Klikni start u prvniho testu. Dela to, ze ma limit asi 70ms a mezitim se pokusi
seradit co nejdelsi pole. Cili, cim delsi rada cisel na radku, tim je algoritmus
rychlejsi. Vidis, ze js kod Timsortu, ktery jsem nekde vygoogloval, hrave
prekonava vestaveny algoritmus javascriptu (u mne ve Firefox webbrowseru, v117).
Jenze, takove mereni je dost pochybne, firefox ma optimalizace pro opakovani
stejneho kodu pomoci procesoru. Ja tam mam treba serii cisel
4 (pro 1.000) 1 1 2 4 8 10 18 39 (pro 100.000), zacal se 4s, pak naraz skocil na
1s, coz je nesmysl, kdyz je pole 2x vetsi
A takovy Tournament sort (ja ho oznacuji jako SelectPyramid), si take nevedl
spatne.
Cili, spravne by mel byt nejrychlejsi vestaveny sort. Coz by byl, kdyby nebylo
optimalizaci a ja ten kod pro testovani nemusel opakovat.
Zobrazeno 46 zpráv z 46.