Permutacja liczb od 0 do N-1 będzie instancją klasy Perm. Wewnętrznie przechowujemy permutację jako listę o długości N.
# Moduł perms.py
class Perm:
"""Klasa reprezentująca permutacje."""
def __init__(self, size, data=None):
"""Konstruktor permutacji."""
self.size = size
if data:
if self.size != len(data):
raise ValueError("different size and len(data)")
self.data = list(data)
else:
self.data = list(range(self.size))
def __str__(self):
"""Napis odpowiadający liście wewnętrznej w permutacji."""
return str(self.data)
def __repr__(self):
"""Reprezentacja napisowa permutacji."""
return "Perm({}, data={})".format(self.size, self.data)
def __mul__(self, other):
"""Mnożenie dwóch permutacji."""
if self.size != other.size:
raise ValueError("different size")
new_perm = Perm(self.size)
for i in range(new_perm.size):
new_perm.data[i] = self.data[other.data[i]]
return new_perm
def __invert__(self):
"""Zwraca permutację odwrotną (~perm)."""
new_perm = Perm(self.size)
for i in range(new_perm.size):
new_perm.data[self.data[i]] = i
return new_perm
def label(self):
"""Tworzenie etykiety dla permutacji, nie działa dla size > 36."""
if self.size > 36:
raise ValueError("problem with labels")
letters = "0123456789abcdefghijklmnopqrstuvwxyz"
tmp = []
for item in self.data:
tmp.append(letters[item])
return "".join(tmp)
def is_identity(self):
"""Zwraca True, jeżeli permutacja jest identycznością."""
return all(self.data[i] == i for i in range(self.size))
def __cmp__(self, other): # tylko Python 2
"""Porównywanie permutacji (leksykograficzne)."""
return cmp(self.data, other.data) # porównywanie list (szybsze)
def max(self):
"""Return the highest element moved by the perm."""
j_max = 0
for j in range(self.size):
if self.data[j] != j:
j_max = j
return j_max
def min(self):
"""Return the lowest element moved by the perm."""
j_min = 0 # zero będzie też dla identyczności
for j in range(self.size):
if self.data[j] != j:
j_min = j
break
return j_min
def __hash__(self):
"""Hashable perms."""
return hash(tuple(self.data))
class TestPerm(unittest.TestCase):
def setUp(self):
self.N = 4
self.E = Perm(self.N)
self.H = Perm(self.N, [1, 3, 0, 2])
def test_init(self):
self.assertEqual(self.H, Perm(self.N, data=[1, 3, 0, 2]))
self.assertRaises(ValueError, lambda: Perm(2, data=[0, 1, 2]))
def test_repr(self):
self.assertEqual(repr(self.E), "Perm(4, data=[0, 1, 2, 3])")
self.assertEqual(repr(self.H), "Perm(4, data=[1, 3, 0, 2])")
def test_label(self):
self.assertEqual(self.E.label(), "0123")
self.assertEqual(self.H.label(), "1302")
def test_identity(self):
self.assertTrue(self.E.is_identity())
self.assertFalse(self.H.is_identity())
def test_mul(self):
self.assertEqual(self.E * self.E, self.E)
self.assertEqual(self.E * self.H, self.H)
self.assertNotEqual(self.H * self.H, self.H)
self.assertRaises(ValueError, lambda: Perm(2) * Perm(1))
def test_invert(self):
self.assertEqual(~self.E, self.E)
self.assertEqual(~self.H, Perm(self.N, [2, 0, 3, 1]))
self.assertEqual(self.H * (~self.H), self.E)
def test_cmp(self):
self.assertFalse(self.H == self.E)
self.assertTrue(self.E < self.H)
self.assertTrue(self.H < self.H * self.H)
def test_min_max(self):
self.assertEqual(self.H.min(), 0)
self.assertEqual(self.H.max(), 3)
self.assertEqual(self.E.min(), 0)
self.assertEqual(self.E.max(), 0)
def test_hash(self):
aset = set()
aset.add(self.E)
aset.add(self.E) # ignored
self.assertEqual(len(aset), 1)
aset.add(self.H)
aset.add(self.H) # ignored
self.assertEqual(len(aset), 2)
def tearDown(self): pass
# Przykład zastosowania.
N = 4 # permutacje liczb od 0 do 3
E = Perm(N) # identyczność
H = Perm(N, [1, 3, 0, 2])
print("identity {} {}".format(E.is_identity(), H.is_identity())) # True, False
print("str {} {}".format(E, H)) # [0, 1, 2, 3] [1, 3, 0, 2]
print("label {} {}".format(E.label(), H.label())) # 0123, 1302
print("invert {} {} {}".format(H, ~H, H*(~H)))
print("cmp {}".format(H < H*H)) # True