patternpythonMinor
Improving efficiency of Project Euler 185 (16-place Mastermind)
Viewed 0 times
placemastermind185projecteulerefficiencyimproving
Problem
This a solution for Project Euler Problem 185. It works fine for the example but slow for the problem statement. How can I improve its speed and efficiency?
```
import itertools
from collections import defaultdict
def mrange(start, stop, step=1):
i = start
while i < stop:
yield i
i += step
#produces the list of numbers
number= mrange(104,105)
newlist=[]
def combination(x,n):
if n==0:
temp=[]
for i in range(1,len(x)+1):
temp.extend(list(itertools.combinations(x,i)))
return temp
return list(itertools.combinations(x,n))
def combineDictTuple(dt):
d = {}
for item in dt:
d.update(item)
return d
def prob(a,n):
temp=[]
if n==0:
temp=number
#modchar is indexing the numbers eg:[{0: '9'}, {1: '0'}, {2: '3'}, {3: '4'}, {4: '2'}]
modchar=[{i: c} for i, c in enumerate(a)]
combos = combination(modchar,n)
newCombos = [combineDictTuple(dt) for dt in combos]
# newCombos gives combination of the indexes
#[{0: '9', 1: '0'}, {0: '9', 2: '3'}, {0: '9', 3: '4'}, {0: '9', 4: '2'}, {1: '0', 2: '3'}, {1: '0', 3: '4'}, {1: '0', 4: '2'}, {2: '3', 3: '4'}, {2: '3', 4: '2'}, {3: '4', 4: '2'}]
for j in number:
for i in newCombos:
index=i.keys()
val=i.values()
# print index,val
if n==0:
if all(str(j)[index[l]]==val[l] for l in range(0,len(val))):
if j in temp:
del temp[temp.index(j)]
break
else:
if all(str(j)[index[l]]==val[l] for l in range(0,len(val))):
temp.append(j)
break
return temp
val='''90342 ;2 correct
70794 ;0 correct
39458 ;2 correct
34109 ;1 correct
51545 ;2 correct
12531 ;1 correct'''
val=val.replace('correct','').replace(';','').split('\n')
for i in val:
i=i.split()
number=prob(
```
import itertools
from collections import defaultdict
def mrange(start, stop, step=1):
i = start
while i < stop:
yield i
i += step
#produces the list of numbers
number= mrange(104,105)
newlist=[]
def combination(x,n):
if n==0:
temp=[]
for i in range(1,len(x)+1):
temp.extend(list(itertools.combinations(x,i)))
return temp
return list(itertools.combinations(x,n))
def combineDictTuple(dt):
d = {}
for item in dt:
d.update(item)
return d
def prob(a,n):
temp=[]
if n==0:
temp=number
#modchar is indexing the numbers eg:[{0: '9'}, {1: '0'}, {2: '3'}, {3: '4'}, {4: '2'}]
modchar=[{i: c} for i, c in enumerate(a)]
combos = combination(modchar,n)
newCombos = [combineDictTuple(dt) for dt in combos]
# newCombos gives combination of the indexes
#[{0: '9', 1: '0'}, {0: '9', 2: '3'}, {0: '9', 3: '4'}, {0: '9', 4: '2'}, {1: '0', 2: '3'}, {1: '0', 3: '4'}, {1: '0', 4: '2'}, {2: '3', 3: '4'}, {2: '3', 4: '2'}, {3: '4', 4: '2'}]
for j in number:
for i in newCombos:
index=i.keys()
val=i.values()
# print index,val
if n==0:
if all(str(j)[index[l]]==val[l] for l in range(0,len(val))):
if j in temp:
del temp[temp.index(j)]
break
else:
if all(str(j)[index[l]]==val[l] for l in range(0,len(val))):
temp.append(j)
break
return temp
val='''90342 ;2 correct
70794 ;0 correct
39458 ;2 correct
34109 ;1 correct
51545 ;2 correct
12531 ;1 correct'''
val=val.replace('correct','').replace(';','').split('\n')
for i in val:
i=i.split()
number=prob(
Solution
Expressiveness
Strategy
Let \$p\$ be the number of positions (16 in the real problem, 5 in the test case).
Let \$g\$ be the number of guesses (22 in the real problem, 6 in the test case).
The outline of
If we estimate that \$c = 3\$, then the complexity of your solution is \$O(10^p p^3g)\$ in time. The maximum space needed is the number candidates remaining after processing the first constraint, or \$O(\left(\begin{array}{c}p\\c\end{array}\right))\$.
I'm not sure that I analyzed the complexity completely correctly, but in any case those bounds are crazy. Your strategy of enumerating all the possibilities just doesn't scale. It's as if you were trying to solve a sudoku puzzle by generating all possible solutions first.
- What does
prob(a,n)mean? How aboutapply_constraint(guess, n_correct)instead?
- Why do you reinvent
xrange()(Python 2) orrange()(Python 3)?
numberis a global variable, which makes the code hard to follow.
numberis an iterable of numbers, but your constraints are given as characters, which makes comparisons awkward.
tempis an uninformative name for a variable. It's especially bad when you returntemp— that implies that it's not very temporary at all.
- Parsing the input is better done using a regular expression.
Strategy
Let \$p\$ be the number of positions (16 in the real problem, 5 in the test case).
Let \$g\$ be the number of guesses (22 in the real problem, 6 in the test case).
The outline of
prob() is:for j in number:
for i in newCombos:
…
[… for l in range(len(newCombos.values()))]number is \$O(10^p)\$ (the number of candidate solutions you initially generate).newCombos has \$\left(\begin{array}{c}p\\c\end{array}\right) = \dfrac{p!}{c!\ (p - c)!}\$ combinations, each of which has \$c\$ values, where \$c\$ is the number of correct guesses within that constraint.If we estimate that \$c = 3\$, then the complexity of your solution is \$O(10^p p^3g)\$ in time. The maximum space needed is the number candidates remaining after processing the first constraint, or \$O(\left(\begin{array}{c}p\\c\end{array}\right))\$.
I'm not sure that I analyzed the complexity completely correctly, but in any case those bounds are crazy. Your strategy of enumerating all the possibilities just doesn't scale. It's as if you were trying to solve a sudoku puzzle by generating all possible solutions first.
Code Snippets
for j in number:
for i in newCombos:
…
[… for l in range(len(newCombos.values()))]Context
StackExchange Code Review Q#54138, answer score: 5
Revisions (0)
No revisions yet.