| Uutiset | Koodikirjasto | Wiki | Keskustelut | FAQ | Info |
Anagrammien muodostustachiman 22.06.04 18:15 Muodostaa anagrammeja komentoriviargumentista. Koodissa on pyritty käyttämään hyödyksi Pythonille ominaisia toimintoja. Sisältää myös pari yleiskäyttöistä listafunktiota.
# -*- coding: cp1252 -*- from sys import argv max_row_length = 78 # Säädellään tulostuksen leveyttä alphabets_fin = 'abcdefghijklmnopqrstuvwxyzåäöABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ' digits = '0123456789' anag_line = [] def factorial(x): """Laske ja palauta x:n kertoma""" f = 1 for a in range(2, x + 1): f *= a return f def unique_order_count(s): """Palauta listan s erilaisten järjestysten lukumäärä""" if len(s) < 2: return len(s) # Duplikaatit listassa vähentävät sen erilaisten järjestysten määrää dupl = [] dupl_count = [] for x in s: if s.count(x) > 1 and x not in dupl: dupl.append(x) dupl_count.append(s.count(x)) # Anagrammien määrä ilman duplikaatteja raw_count = factorial(len(s)) # Duplikaateista johtuva vähennyskerroin (eri duplikaattien lukumääristä # otetaan kertomat ja muodostetaan näistä tulo) dupl_factor = reduce(lambda a, b: a * b, map(factorial, dupl_count), 1) return raw_count / dupl_factor def get_next_order(s): """Palauta lista seuraavassa järjestyksessä Lajittele lista s uuteen järjestykseen niin, että uusi järjestys on aakkosjärjestyksessä nykyisestä seuraava mahdollinen ja palauta se. Mikäli uutta järjestystä ei voida muodostaa (järjestys on jo valmiiksi käänteinen aakkosjärjestys), palauta tyhjä lista. Esimerkkejä: ['a', 'b', 'c'] -> ['a', 'c', 'b'] ['matti', 'teppo', 'seppo'] -> ['seppo', 'matti', 'teppo'] [45, 27, 9] -> [] """ if len(s) < 2: return [] # Etsitään listan lopusta päin ensimmäinen alkio, joka on oikeanpuoleistaan # pienempi. Tämä alkio toimii rajana; listan alkuosaan ei tarvitse koskea. # Esim. "astut"-sanassa keskimmäinen t < u for x in range(len(s) - 2, -1, -1): if s[x] < s[x + 1]: # Vaihdetaan kyseiselle paikalle listan loppupuolelta # aakkosjärjestyksessä seuraava alkio. Tutkiminen aloitetaan # listan lopusta, koska loppulista on kyseiseen alkioon asti # käänteisessä järjestyksessä. Viimeistä alkioita ei voi ottaa # automaattisesti, koska kyseessä voi olla duplikaatti; esim. # järjestys "astut" ei muuttuisi, jos t-kirjaimien paikkaa # vaihdettaisiin. for y in range(len(s) - 1, x, -1): if s[y] > s[x]: s[x], s[y] = s[y], s[x] # Listan loppuosa aakkosjärjestykseen s_end = s[x + 1:] s_end.sort() return s[:x + 1] + s_end return [] # Lista oli jo käännetyssä aakkosjärjestyksessä def get_orig_word(): """Poimi komentoriviltä sana, josta anagrammit muodostetaan""" quiet_mode = 0 # Kysytään tarvittaessa varmistus pitkästä tulosteesta if len(argv) > 1: orig = argv[len(argv) - 1] # Käytetään viimeistä argumenttia else: print 'Anna komentoriviargumenttina sana, josta haluat anagrammeja' print 'Käyttö: python anag.py [-q] sana' print ' -q = älä kysy varmistusta yli tuhannen rivin tulostuksesta' return [] if len(argv) > 2 and argv[len(argv) - 2] == '-q': quiet_mode = 1 # Käyttäjän syöte listaksi, vain aakkoset ja numerot otetaan mukaan # Esim. "linna" -> ['l', 'i', 'n', 'n', 'a'] chars = [x for x in orig if x in alphabets_fin + digits] anags = unique_order_count(chars) if anags < 2: print 'Virhe - Käytä enemmän aakkosia tai numeroita' return [] # Lasketaan kuinka monta riviä tavaraa tulee for words_in_row in range(1, max_row_length / 2): # Sanoja rivillä * (sanan pituus + väli) - 1 puuttuva väli lopusta. # Katsotaan onko yksi sana lisää riville jo liikaa. if (words_in_row + 1) * (len(chars) + 1) - 1 > max_row_length: row_count = anags / words_in_row if anags % words_in_row > 0: row_count += 1 # Yksi vajaa rivi vielä mukaan break if row_count > 1000 and quiet_mode == 0: if raw_input('Haluatko varmasti tulostaa ' \ '%d anagrammiriviä (k/e)? ' % (row_count)) != 'k': return [] return chars def print_anag(chars): """Tulosta anagrammit siististi sarakkeisiin Puskuroi kirjainlistana annettavat sanat ja tulosta ne vasta kun rivi tulee täyteen. Jos argumenttina saadaan tyhjä lista, tulosta rivipuskuri tyhjäksi joka tapauksessa. """ global anag_line if len(chars) == 0: # Tulostetaan puskuri tyhjäksi if len(anag_line) > 0: print ' '.join(anag_line) # Erotetaan sanat välilyönneillä else: anag_line.append(''.join(chars)) # Uusi sana listaan if len(anag_line) * (len(anag_line[0]) + 1) - 1 > max_row_length: # Kun lista on liian pitkä yhdelle riville, tulostetaan siitä... print ' '.join(anag_line[:-1]) # ...muut paitsi vika alkio... anag_line = anag_line[-1:] # ...ja jätetään vain se enää listaan. def create_anagrams(): chars = get_orig_word() chars.sort() # Edetään aakkosjärjestyksestä kohti käänteistä sellaista while chars != []: print_anag(chars) chars = get_next_order(chars) print_anag([]) # Tulosta mahdollinen kesken jäänyt rivi create_anagrams() moshe 23:32 16.1.06 eikö ton factorialin vois tehä helpommin: reduce(lambda y, z: y*z, range(x)) ? chiman 10:23 14.6.06 Voisi toki, kunhan lisää ykkösen alkuarvoksi, jotta 0! lasketaan oikein. (Tässä koodissa tuota ei kutsuta nollalla koskaan, mutta parempi silti tehdä oikein.) Muutenkin tämä skripti kaipaisi siistimistä. Kielen opettelu oli tuolloin pari vuotta sitten vielä kesken, eikä kaikkia kohtia ole tehty pythonmaisesti. Mm. get_next_order-funktion tehtävät voisi hoitaa generaattorilla. Parannan koodia lähiaikoina. |
![]() Haku
|