-
Notifications
You must be signed in to change notification settings - Fork 1
/
bitfield_candidates.py
112 lines (95 loc) · 2.55 KB
/
bitfield_candidates.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import re
import sys
from bitfield_dp import bitfields_dynamic_programming
from bitfield_powerset import find_subsets_backtracking
is_lowercase_letters = re.compile('^[a-z_\-]+$')
LETTER_FREQUENCIES = {
"e": 25,
"t": 24,
"a": 23,
"o": 22,
"i": 21,
"n": 20,
"s": 19,
"h": 18,
"r": 17,
"d": 16,
"l": 15,
"c": 14,
"u": 13,
"m": 12,
"w": 11,
"f": 10,
"g": 9,
"y": 8,
"p": 7,
"b": 6,
"v": 5,
"k": 4,
"j": 3,
"x": 2,
"q": 1,
"z": 0
}
def word_to_bitfield(word):
word = word.lower()
if not is_lowercase_letters.match(word):
return False
bitfield = 0
for character in word:
index = LETTER_FREQUENCIES[character]
shifted = (1 << index)
if bitfield & shifted:
return False
bitfield = bitfield | shifted
return bitfield
# assuming a word-per-line dictionary with no dupes
def read_dictionary_file(filename):
bitfield_dictionary = {}
with open(filename) as f:
for word in f:
word = word.strip()
bitfield = word_to_bitfield(word)
if bitfield != False:
if bitfield_dictionary.get(bitfield) == None:
bitfield_dictionary[bitfield] = []
bitfield_dictionary[bitfield].append(word)
return bitfield_dictionary
def find_first_letter(bitfield):
first_letter = 0
while bitfield & 1 == 0:
first_letter += 1
bitfield = bitfield >> 1
return first_letter
def find_first_letter_needed(bitfield):
first_letter = 0
while bitfield & 1 == 1:
first_letter += 1
bitfield = bitfield >> 1
return first_letter
def pangrams_from_radix(radixes, objective, accumulated=0, words=[]):
if accumulated == objective:
print words
return
first_letter_needed = find_first_letter_needed(accumulated)
bitfields_with_letter = radixes[first_letter_needed]
for bitfield in bitfields_with_letter:
if accumulated & bitfield == 0:
pangrams_from_radix(radixes, objective, accumulated | bitfield, words + [bitfield])
def generate_pangrams(bitfield_dictionary):
# find all combinations of keys that mask to 26 'one' bits
bitfield_keys = bitfield_dictionary.keys()
radixes = []
for i in range(26):
radixes.append([])
for bitfield in bitfield_keys:
first_letter = find_first_letter(bitfield)
radixes[first_letter].append(bitfield)
saturated_bitmask = (1 << 26) - 1
pangrams_from_radix(radixes, saturated_bitmask)
if __name__ == "__main__":
filename = "tests/scrabble-dictionary.txt"
if len(sys.argv) > 1:
filename = sys.argv[1]
bitfield_dictionary = read_dictionary_file(filename)
generate_pangrams(bitfield_dictionary)