Solving problems in Python - python

I currently have the following, both of which must hold true:
A B C D + A E F = E F G H and
I C J * E = A B C D
Each letter represents a unique digit from 0 to 9 and both equations must hold true.
I need to write a Python solution which outputs the correct answer, here is my code:
import numpy as np
def solve():
for a in range(0,10):
for b in range(0,10):
for c in range(0,10):
for d in range(0,10):
for e in range(0,10):
for f in range(0,10):
for g in range(0,10):
for h in range(0,10):
for i in range(0,10):
for j in range(0,10):
if len(set([a, b, c, d, e, f, g, h, i, j])) == 10:
icj = 100*i + 10*c + j
e = e
abcd = 1000*a + 100*b + 10*c + d
aef = 100*a + 10*e + f
efgh = 1000*e + 100*f + 10*g + h
if icj * e == abcd and abcd + aef == efgh:
print(icj, e, abcd, aef, efgh)
print(solve())
However, when I run this, not only does it take a while to run, it outputs "None". Any ideas as to where I am going wrong?

You should try for x in range(0, 10) instead of for x in range(0,9) because you were looping from 0 to 8
If you want to loop in a more efficient way, you can use permutations:
from itertools import permutations
for a, b, c, d, e, f, g, h, i, j in permutations(range(0, 10), 10):
print(a, b, c, d, e, f, g, h, i, j)
Result :
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 9 8
...
9 8 7 6 5 4 3 2 0 1
9 8 7 6 5 4 3 2 1 0
Here is the final code :
import numpy as np
from itertools import permutations
def solve():
for a, b, c, d, e, f, g, h, i, j in permutations(range(0, 10), 10):
icj = 100*i + 10*c + j
e = e
abcd = 1000*a + 100*b + 10*c + d
aef = 100*a + 10*e + f
efgh = 1000*e + 100*f + 10*g + h
if icj * e == abcd and abcd + aef == efgh:
print(icj, e, abcd, aef, efgh)
print(a, b, c, d, e, f, g, h, i, j)
solve()
Output :
934 7 6538 672 7210
6 5 3 8 7 2 1 0 9 4

Apart from the typos, if you only test in the very inner loop whether all 10 digits are different, this inner loop is executed 1010 = 10,000,000,000 times. If you test at every pass, you "only" need 10! = 3,628,800 passes to this inner loop.
You still can do better changing the order of variables, so the equation abc * d == hibj can be tested without needing the other 3 variables, and only go deeper when it holds. For these 7 digits, you enter 604,800 times in that loop, and only 45 times you need to go deeper to reach the most inner loop only 270 times.
def solve():
for a in range(0, 10):
for b in range(0, 10):
if a != b:
for c in range(0, 10):
if not c in [a, b]:
for d in range(0, 10):
if not d in [a, b, c]:
for h in range(0, 10):
if not h in [a, b, c, d]:
for i in range(0, 10):
if not i in [a, b, c, d, h]:
for j in range(0, 10):
if not j in [a, b, c, d, h, i]:
abc = 100 * a + 10 * b + c
hibj = 1000 * h + 100 * i + 10 * b + j
if abc * d == hibj:
print(abc, '*', d, '=', hibj)
for e in range(0, 10):
if not e in [a, b, c, d, h, i, j]:
for f in range(0, 10):
if not f in [a, b, c, d, h, i, j, e]:
for g in range(0, 10):
if not g in [a, b, c, d, h, i, j, e, f]:
hde = 100 * h + 10 * d + e
defg = 1000 * d + 100 * e + 10 * f + g
if hibj + hde == defg:
print(abc, d, hibj, hde, defg)
solve()
print('done')
Although it now runs fast enough, still more specific optimizations can be thought of:
Change the order to a,b,c and h,i,j then calculate whether hibj is a multiple of abc. Only in case it is, this defines d which should be between 0 and 9, and different from the rest.
Or, the reverse: generate a,b,c,d and then try all the multiples first whether they fit b and then whether the corresponding h,i,j are different from each other and different from a,b,c,d.
h should be smaller than a, otherwise d will be larger than 9. This makes a at least 1.
Usually in this kind of problems, the first digit of every number is supposed to be non-zero, which can further reduce the number of checks.
An alternative approach, is to use an SMT/SAT solver such as Z3. With such a solver, all the conditions are formulated, and via all kind of heuristics a solution is searched for. Example codes: here and here.
This is how the code could look like:
from z3 import Int, And, Or, Distinct, Solver, sat
D = [Int(f'{c}') for c in "abcdefghij"]
a, b, c, d, e, f, g, h, i, j = D
vals_0_to_9 = [And(Di >= 0, Di <= 9) for Di in D]
all_different = [Distinct(D)]
abc = 100 * a + 10 * b + c
hibj = 1000 * h + 100 * i + 10 * b + j
hde = 100 * h + 10 * d + e
defg = 1000 * d + 100 * e + 10 * f + g
equations = [abc * d == hibj, hibj + hde == defg]
s = Solver()
s.add(vals_0_to_9 + all_different + equations)
while s.check() == sat:
m = s.model()
print(", ".join([f'{Di}={m[Di]}' for Di in D]))
s.add(Or([Di != m[Di] for Di in D]))
This prints out a=9, b=3, c=4, d=7, e=2, f=1, g=0, h=6, i=5, j=8 as unique solution.

Related

projecteuler problem 8 - unsure on what is wrong with my code

A Pythagorean triplet is a set of three natural numbers, a < b < c,
for which,
a^2 + b^2 = c^2
For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2.
There exists exactly one Pythagorean triplet for which a + b + c =
1000. Find the product abc.
above is the question. when i run my code it works and runs how i expect it to, but the programme always finishes without finding an answer. any advice on how to improve my code would be appreciated.
for k in range (1,1000):
c = 1000 - k
for i in range (1,c):
b = c - i
c_sqr = c ** 2
b_sqr = b ** 2
a = c - b
a_sqr = a ** 2
if a_sqr + b_sqr == c_sqr:
if a + b + c == 1000:
product = a * b * c
print(f"{product} is the answer")
exit()
else:
print("Pythagorean triplet!")
else:
josephmama = 11
print("not a pythagorean triplet")
In your code c < 1000 and a + b == c are invariants. The sum (a + b + c) == 2 * c requires that the longest side(hypotenuse) of the right triangle to be as long as the sum of the other sides, and there's no such numbers that satisfy it and so the if body never executes.
for a in range(1, 1000):
for b in range(1, 1000):
c = 1000 - (a + b)
if (a ** 2 + b ** 2 == c ** 2):
print(a * b * c)
exit()
a is not always equal to c - b
by deriving b from c and a from c and b you are unnecessarily limiting the options of what b and a could be. Just loop through all combinations of a, b and c, add the constrains you have and you'll eventually get your triplet.
for a in range(1, 1000):
for b in range(1, 1000):
for c in range(1, 1000):
if a+b+c == 1000:
if a**2+b**2 == c**2:
if a < b:
if b < c:
print(f"My Triplet: a: {a}, b: {b}, c:{c}")

speeding up routine with eight nested for loops in any way possible

I have a function, _get_result, which takes in shape (L , L) np.complex64 arrays B, evec, f_mat, a shape (L) np.complex64 array ener, and other float32s, and returns some np.complex64.
Currently, it goes through 8-for loops. For each loop, it constructs matrices depending on the looping indices via _get_elements, then calculates permanent of these matrices by _perm.
As you might imagine for an 8-for loop routine, it is painfully slow. I'd like to have a code to goes to L ~ 64 in a reasonable computation time (less than say, a couple days), but even after using numba to accelerate the code, already at L = 4, it takes ~ 1 sec, L = 8 ~ 200 sec, and L = 16 ~ 3 hrs, so I don't think it will scale well in this state.
(I know that permanents of matrices scale O(eⁿ), but I am calculating permanents of quite small matrices (4x4 at maximum), so I don't think that's the problem.)
Is there a way to speed up this code using any method? I'm wondering if I made any of the for-loops redundantly, or I could calculate these quantities any faster, or if there's anything I can do with parallelisation, utilising GPUs, or anything else.
Below is the code:
import numpy as np
from numba import njit, prange
dtype_complex = np.complex64
#njit
def _perm(matrix):
n = matrix.shape[0]
d = np.ones(n, dtype=dtype_complex)
j = 0
s = 1
f = np.arange(n)
v = matrix.sum(axis=0)
p = np.prod(v)
while (j < n - 1):
v -= 2 * d[j]*matrix[j]
d[j] = -d[j]
s = -s
prod = np.prod(v)
p += s * prod
f[0] = 0
f[j] = f[j+1]
f[j+1] = j + 1
j = f[0]
return p / 2 ** (n - 1)
#njit
def _get_elements(one_over_det, matrix, lefts, rights):
length = len(lefts)
to_perm = np.zeros((length, length), dtype=dtype_complex)
for idx1, lbit_idx1 in enumerate(lefts):
for idx2, lbit_idx2 in enumerate(rights):
to_perm[idx1, idx2] = one_over_det * matrix[lbit_idx2, lbit_idx1]
return _perm(to_perm)
#njit
def _get_result(t, L, B, ener, evec, f_mat, I, site1, site2):
result = 0.0 + 0.0j
for a in np.arange(L):
for b in np.arange(L):
for e in np.arange(L):
for f in np.arange(L):
H_abef = 2 * (B[:,a] - B[:,b] + B[:,e] - B[:,f])
f_exp_H_abef_t = f_mat # (np.diag(np.exp(1j * H_abef * t)).astype(dtype_complex))
to_inv = I + f_mat - f_exp_H_abef_t
inv = np.linalg.inv(to_inv)
one_over_det = 1.0 / np.linalg.det(to_inv)
matrix = f_exp_H_abef_t # inv
phase1 = np.exp(1j * (
ener[a] - ener[b] + ener[e] - ener[f]
- B[a, a] - B[b, b] - B[e, e] - B[f, f]
+ 2 * (
+ B[a, b] + B[e, f]
+ B[f, a] + B[f, b]
- B[e, a] - B[e, b]
)
) * t)
for c in np.arange(L):
for d in np.arange(L):
phase2 = np.exp(2j * (
B[f, c] + B[f, d] - B[e, c] - B[e, d]
) * t)
for g in np.arange(L):
for h in np.arange(L):
# 8th order term
elements = _get_elements(one_over_det, matrix, [a, c, e, g], [b, d, f, h])
# 6th order terms
if b == c: elements += _get_elements(one_over_det, matrix, [a, e, g], [d, f, h])
if d == e: elements += _get_elements(one_over_det, matrix, [a, c, g], [b, f, h])
if b == e: elements += _get_elements(one_over_det, matrix, [a, c, g], [d, f, h])
if f == g: elements += _get_elements(one_over_det, matrix, [a, c, e], [b, d, h])
if d == g: elements += _get_elements(one_over_det, matrix, [a, c, e], [b, f, h])
if b == g: elements += _get_elements(one_over_det, matrix, [a, c, e], [d, f, h])
# quartic terms
if b == e and f == g: elements += _get_elements(one_over_det, matrix, [a, c], [d, h])
if b == e and d == g: elements += _get_elements(one_over_det, matrix, [a, c], [f, h])
if b == c and f == g: elements += _get_elements(one_over_det, matrix, [a, e], [d, h])
if b == c and d == g: elements += _get_elements(one_over_det, matrix, [a, c], [f, h])
if d == e and f == g: elements += _get_elements(one_over_det, matrix, [a, c], [b, h])
if b == c and d == e: elements += _get_elements(one_over_det, matrix, [a, g], [f, h])
if d == e and b == g: elements += _get_elements(one_over_det, matrix, [a, c], [f, h])
# quadratic term
if b == c and d == e and f == g: elements += one_over_det * matrix[h, a]
unitary = evec[site2, a] * evec[site2, b] \
* evec[site1, c] * evec[site1, d] \
* evec[site2, e] * evec[site2, f] \
* evec[site1, g] * evec[site1, h]
result += unitary * phase1 * phase2 * elements
return result
Thanks a lot in advance.

Can you create multiple variables in one line in Python 3.6

I have a lot of variables that are all the same value but they need different so they can be differentiated.
I haven't tried anything yet.
Could you do this:
a, b, c = []
e, f, g = 1
You can do:
a = b = c = []
e = f = g = 1
Just they are the same objects.
But you can also do:
a, b, c = [[]] * 3
e, f, g = [1] * 3
But to not have them be the same objects:
a, b, c = [[] for i in range(3)]
e, f, g = [1 for i in range(3)]

print reverse pattern of alphabets using functions in python

I have written following code:
def contalpha(n):
num = 65
for i in range(0, n):
for j in range(0, i+1):
ch = chr(num)
print(ch, end=" ")
num = num +1
print("\r")
n = 7
contalpha(n)
The output is:
A
B C
D E F
G H I J
K L M N O
P Q R S T U
V W X Y Z [ \
but what I want is:
A B C D E
A B C D
A B C
A B
A
How can I make it?
I'd advise against using chr. Ascii can be confusing, instead just use a string of all capital ascii characters (which is a sequence of characters, and can be handily found in the string module).
import string
def contalpha(n):
for i in range(n, 0, -1):
print(*string.ascii_uppercase[:i], sep=' ')
contalpha(5)
outputs:
A B C D E
A B C D
A B C
A B
A
You need to reverse the range in order to start from the bigger row range(0, n)[::-1]. Then you need to set num = 65 every time you start a new row for it to always start from A.
There you go:
def contalpha(n):
num = 65
for i in range(0, n)[::-1]:
for j in range(0, i+1):
ch = chr(num)
print(ch, end=" ")
num = num +1
num = 65
print("\r")
n = 7
contalpha(n)
Output
A B C D E F G
A B C D E F
A B C D E
A B C D
A B C
A B
A
Try this:
def contalpha(n):
for i in range(n, 0, -1):
num = 65
for j in range(0, i):
ch = chr(num)
print(ch, end=" ")
num = num +1
print("\r")
n = 7
contalpha(n)
First you need to set num to 65 every outer loop to get alphabets from A again and second you should reverse outer loop range to print from max size to min size.
output:
A B C D E F G
A B C D E F
A B C D E
A B C D
A B C
A B
A

Python - Make avarage variables without 0

A = 0
B = 10
C = 20
N = [(A, B, C)]
Avarage = (A + B + C) / sum(1 for i in N if i != 0)
Avarage = 30??
Need Avarage = 15.
Any idea?
You need to remove ( and ) from your list N
a = 0
b = 10
c = 20
n = [a, b, c]
list_avg = sum(n) / sum(1 for i in n if i != 0)
Output:
>>> list_avg
15
N is list containing tupple. So sum(1 for i in N if i != 0) returns 1 as there is only 1 element (a tupple) in list N.
Try:
A = 0
B = 10
C = 20
N = [(A, B, C)]
Avarage = (A + B + C) / sum(1 for i in N[0] if i != 0)
print Avarage
15
OR:
A = 0
B = 10
C = 20
N = [A, B, C]
Avarage = (A + B + C) / sum(1 for i in N if i != 0)
print Avarage
15
As stated by depperm, N is a list of one tuple, so you have in your sum only one element (which is different than 0 because it is a tuple and not an int). Otherwise you can use numpy
import numpy as np
average = np.mean([A, B, C])
Make a new list of values without the zeros, then average that:
non_zero = [v for v in (A, B, C) if v != 0]
avg = sum(non_zero)/len(non_zero)

Categories

Resources