Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: Listan alkioiden järjestelyä Pythonilla

ittna [20.12.2006 00:51:28]

#

Kuinka saisin tehtyä ohjelman, joka muodostaa annetun listan alkioista kaikki mahdolliset järjestykset. Alkioiden määrää ei tunneta.
Eli esim.

[a,b,c]
[a,c,b]
[b,a,c]
[b,c,a]
[c,a,b]
[c,b,a]

Metabolix [20.12.2006 06:58:30]

#

Rekursio on aina yksi tapa.

Antti Laaksonen [20.12.2006 09:41:08]

#

Tässä on esimerkki:

def perm(lista, tulos):
    if len(lista) == len(tulos):
        print tulos
        return
    for alkio in lista:
        if alkio not in tulos:
            perm(lista, tulos + [alkio])

perm(['a', 'b', 'c'], [])

Funktio perm valitsee listasta yhden alkion, joka ei ole vielä tuloslistassa. Sitten funktio perm kutsuu itseään uudestaan. Kun tuloslistan pituus on yhtä suuri kuin alkuperäisen listan pituus, yksi permutaatio on valmis.

ittna [22.12.2006 11:53:10]

#

Aikasta elegantti ratkaisu etten sanoisi.
Kiitän ja kumarran.

Pekka Karjalainen [31.12.2006 13:00:56]

#

Tämä olisi kaksi riviä Haskellilla, mutta minun Python-taitoni eivät riittäneet ihan yhtä eleganttiin ratkaisuun. Ehkä ei pitäisi matkia aivoja jalostavaa Haskell-kieltä, vaan tehdä asiat oikein Python-tavalla. Tässä kuitenkin väärä tapa, joka sentään hoitaa duplikaatit oikein, eikä tukehdu tyhjään listaankaan.

Generaattorien käyttö mahdollistaa isonkin joukon alun katselun. Vielä voisi laajentaa käsittelyä kaikkien osajoukkojen permutaatioihin, mutta sitä ei ole tässä.

#!/usr/bin/env python
# tarvitaan set, eli Python 2.4 tai uudempi
# muuten seuraava voi auttaa

try:
    x = set
except NameError:
    from sets import Set as set
else:
    del x

def nub (list_):
  "Removes duplicates from a list, keeping order."
  seen = set()
  result = []
  for item in list_:
    if item not in seen:
      result.append(item)
      seen.add(item)
  return result

def deleted (list_, item):
  "Return a list with the first occurrence of item deleted."
  copy = list(list_)
  copy.remove(item)
  return copy

def perm_gen(list_):
  "Make a generator over the permutations of a list. Handles duplicates."
  # -- Haskell:
  # perms [] = [[]]
  # perms ls = [a:b | a <- nub ls, b <- perms $ delete a ls]
  #
  if len(list_)==0: return (nothing for nothing in [])
  if len(list_)==1: return ([item] for item in list_)
  return ([a]+b for a in nub(list_)
                for b in perm_gen(deleted(list_,a)))

# testaus

for p in perm_gen(list("ittna")):
  print "".join(p)

print "*"*40

count = 0
for p in perm_gen(list("ohjelmointiputka")):
  if count == 100: break
  count += 1
  print "".join(p)

print "*"*40
print "empty list"
for p in perm_gen([]):
  print p

print "program finished"

# eof

Erityisesti tuo tyhjän generaattorin idiomini on luultavasti ihan typerä (nollan pituisille listoille). Joku oikea Pythonisti kertokoon paremman tavan, jos sellainen on.

Pekka Karjalainen [01.01.2007 18:24:59]

#

Taas esittelin julkisesti tyhmyyttäni. Oikeampi tapa:

def perm_gen(list_):
  "Make a generator over the permutations of a list. Handles duplicates."
  # -- Haskell:
  # perms [] = [[]]
  # perms ls = [a:b | a <- nub ls, b <- perms $ delete a ls]
  #
  if len(list_)==0: return (mt for mt in [[]])
#  if len(list_)==1: return ([item] for item in list_)
  return ([a]+b for a in nub(list_)
                for b in perm_gen(deleted(list_,a)))

Kommentoidun rivin voi poistaa kokonaan ja tyhjän listan permutaatiot voi käsitellä oikein. Johan se on oleellisesti sama ratkaisu molemmissa. Nub-funktiossa myös ei tarvitse käyttää set()-oliota, jos listat ovat lyhyitä. Alkiot voi tarkistaa myös result-listasta.

On se vain niin vaikea tajuta ero tyhjän listan ja listan, joka sisältää tyhjän listan välillä. Toivottavasti lukijaa ei tällainen hämmennys vaivaa.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta