Aristotle Number Puzzle Explanation - python

P:S: I learned from this meta thread that code explanations can also be asked on Stack Overflow.
I am trying to understand a solver for the Aristotle Number puzzle from this site. I understood the point till when we do row reduction using Gaussian elimination and found the following:
a = 76 - j - k - n - 2o - p - r - s
b = j + n + o
c = -38 + k + o + p + r + s
d = j + k + o
e = -38 + k + n + o + p + r
f = 38 - j - k - n - o - p
g = 38 - k - o - r
h = -38 + n + o + p + r + s
i = 38 - j - k - n - o - r
l = 38 - p - s
m = 38 - n - o - p
q = 38 - r - s
The author of the code then continues:
Now, take each permutation of size 7 from {1, 2, ..., 19}, assign it to the independent variables, generate the dependent ones and test against the constraints until finding solutions.
I am really not understanding this concept. Especially on this c file, I am not understanding the following two functions:
bool next_permutation(void)
{
for (int x = 6; x >= 0; x--) {
indices[x]++;
if (indices[x] == 19) {
if (!x) {
return false;
}
moveback(x, 18);
indices[x] = x;
continue;
}
swap(x, indices[x]);
break;
}
j = elem[0];
k = elem[1];
n = elem[2];
o = elem[3];
p = elem[4];
r = elem[5];
s = elem[6];
return true;
}
// adds values to set
// returns true if value successfully added; false otherwise
bool add(int value)
{
if (value > 19 || value < 1) {
return false;
}
int bit = 1 << value;
if (set & bit) {
return false;
}
set |= bit;
return true;
}
I would highly appreciate if someone can help me in understanding this solver. Note the author used python script for row reduction.

I think Gaussian elimination is just a method of reducing a set of equations by substituting one equation into the others to eliminate unnecessary variables. For example you could take the first of the 19 equations and turn it into an equation for a:
a = 38 - b - c
And then substitute that into the other 18. This would eliminate a and reduce your equations to 18. Then repeat until you can't eliminate any more.
It's a nice problem and it gave me an excuse to try out the SymPy package.
Here's some code that creates the equations in SymPy and then uses SymPy's solve function to reduce the equations.
from sympy import symbols, solve
from sympy.parsing.sympy_parser import parse_expr
# Number of unknowns
n = 19
variable_names = [chr(c + 97) for c in range(19)]
# Create SymPy variables
variables = symbols(variable_names)
print("\n{} unknowns:".format(len(variables)))
print(variables)
# These strings define the equations to be solved
hexagon_rows = [
'abc',
'defg',
'hijkl',
'mnop',
'qrs',
'cgl',
'bfkp',
'aejos',
'dinr',
'hmq',
'lps',
'gkor',
'cfjnq',
'beim',
'adh'
]
def make_expression(chars, rhs=0):
return parse_expr('+'.join(list(chars)) + '-' + str(rhs))
expressions = []
for chars in hexagon_rows:
expressions.append(make_expression(chars, 38))
print("\n{} equations to solve:".format(len(expressions)))
for expr in expressions:
print("{} = 0".format(expr))
# Try to solve the equations
# (They can't be solved but SymPy reduces them down)
reduced_expressions = solve(expressions)
print("\nReduced to {} equations:".format(len(reduced_expressions)))
for var, expr in reduced_expressions.items():
print("{} = {}".format(var, expr))
Output:
19 unknowns:
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s]
15 equations to solve:
a + b + c - 38 = 0
d + e + f + g - 38 = 0
h + i + j + k + l - 38 = 0
m + n + o + p - 38 = 0
q + r + s - 38 = 0
c + g + l - 38 = 0
b + f + k + p - 38 = 0
a + e + j + o + s - 38 = 0
d + i + n + r - 38 = 0
h + m + q - 38 = 0
l + p + s - 38 = 0
g + k + o + r - 38 = 0
c + f + j + n + q - 38 = 0
b + e + i + m - 38 = 0
a + d + h - 38 = 0
Reduced to 12 equations:
j = b - n - o
k = e - n - o - p - r + 38
l = -p - s + 38
i = -b - e + n + o + p
c = e - n + s
f = -b - e + n + o + r
g = -e + n + p
q = -r - s + 38
m = -n - o - p + 38
h = n + o + p + r + s - 38
d = b + e - 2*n - o - p - r + 38
a = -b - e + n - s + 38
Hope that helps. With the first part at least!

Related

Solving system of nonlinear (and nonpolynomial) equations

I would like to use python/sympy to solve simple systems of equations coming from impedance calculations in electronics.
In such calculations, due to the "parallel impedance" formula, one often has to deal with expressions of the form:
par(x,y) := (x*y)/(x+y)
Now I have tried with the following code:
from sympy import *
def par(var1,var2):
return (var1 * var2)/(var1+var2)
A = Symbol('A')
B = Symbol('B')
C = Symbol('C')
D = Symbol('D')
E = Symbol('E')
eq1 = A + par(B+50 , (C+ par(D, (E+50)) )) - 50
eq2 = B + par(A+50 , (C+ par(D , (E+50)) )) - 50
eq3 = E + par(D+50, (C+ par(A+50, B+50)) ) - 50
thus defining a system of three equations in five variables {A,B,C,D,E}, but then running
solve([eq1,eq2,eq3], A, B,C,D,E)
the computations just does not terminate.
Do you have any suggestions on how I could approach these type of equations?
Basically polynomials with division by polynomials, with solutions in the complex numbers.
Taking the suggestion of Oscar and focussing on A, B and C you can get a solution for them in terms of D and E:
>>> solve((eq1, eq2, eq3), A, B, C)[0] # one solution
(
50*(D + E)*(D + E + 50)/(3*D**2 - 2*D*E + 150*D - 3*E**2 - 50*E + 5000),
50*(D + E)*(D + E + 50)/(3*D**2 - 2*D*E + 150*D - 3*E**2 - 50*E + 5000),
(-3*D**3*E + 50*D**3 + 2*D**2*E**2 - 500*D**2*E + 10000*D**2 + 3*D*E**3 + 50*D*E**2 - 25000*D*E + 500000*D + 200*E**3 - 5000*E**2 - 500000*E + 12500000)/(3*D**3 + D**2*E + 150*D**2 - 5*D*E**2 + 100*D*E + 5000*D - 3*E**3 - 50*E**2 + 5000*E))
Notice that the solution for A and B is the same (consistent with the symmetry of the first two equations wrt A and B).
>>> sol = Dict(*zip((A,B,C),_))
>>> sol[A] = sol[B]
True
In this form, you can directly substitute values for D and E:
>>> sol.subs({D:1, E:S.Half})
{A: 1030/1367, B: 1030/1367, C: 2265971/1367}
You can also see what relationships between D and E are forbidden by solving for when any of the denominators are 0:
>>> from sympy.solvers.solvers import denoms
>>> set([j.simplify() for i in denoms(sol) for j in solve(i,D)])
{-E, E/3 - sqrt(10*E**2 - 9375)/3 - 25, E/3 + sqrt(10*E**2 - 9375)/3 - 25}
You could also try the Z3 library:
from z3 import Reals, Solver, sat, set_option
def par(var1, var2):
return (var1 * var2) / (var1 + var2)
vars = Reals('A B C D E')
A, B, C, D, E = vars
set_option(rational_to_decimal=True, precision=30)
s = Solver()
s.add(A + par(B + 50, (C + par(D, (E + 50)))) == 50)
s.add(B + par(A + 50, (C + par(D, (E + 50)))) == 50)
s.add(E + par(D + 50, (C + par(A + 50, B + 50))) == 50)
if s.check() == sat:
m = s.model()
print(m)
I get following output:
[D = 0.502507819412956050111927878839?,
C = 0.125,
E = 50.188198722936229689352204927638?,
B = -50.625,
A = -50.625]
The question mark at the end of D's and E's values means they have been approximated.
If you then try the values for A, B and C into the original code, sympy gives two exact expressions:
[{A: -405/8,
B: -405/8,
C: 1/8,
D: -31857/1292 + 5*sqrt(676050154)/5168,
E: 443/1304 + 5*sqrt(676050154)/2608},
{A: -405/8,
B: -405/8,
C: 1/8,
D: -5*sqrt(676050154)/5168 - 31857/1292,
E: 443/1304 - 5*sqrt(676050154)/2608}]

Speeding up perfect swap calculation - avoiding loops

import sys
t=(int(sys.stdin.readline()))
for i in range(0,t):
n=int(sys.stdin.readline())
c=0
s=n*(n+1)/2
if s%2!=0:
print(0)
else:
c=0
i=-1
a=[i for i in range(1,n+1)]
h=s//2
m=0
s1=0
for i in range(n-1,-1,-1):
s1+=a[i]
c+=1
if s1==h:
m=1
break
if s1>h:
break
if m==1:
s1=((c+1)*(2+((c-1)-1)))//2+((n-c-1)*(2+((n-c-1)-1)))//2
print(s1)
else:
print(c)
I am new to python , How can i write this code with using for loop? i don't want to use for loop because i get TLE error. Thanks in advance
Here is the question :
N. Consider the sequence sequence=(1,2,…,N). You should choose two elements of this sequence and swap them.
A swap is perfect if there is an integer o (1≤o<N) such that the sum of the first M elements of the resulting sequence is equal to the sum of its last N−o elements. Find the number of perfect swaps.
i got interested in the problem and found this so far:
a slow version that creates list and really does swap elements is this:
from itertools import combinations
def slow(N):
found = 0
for i, j in combinations(range(N), 2):
lst = list(range(1, N + 1))
lst[i], lst[j] = lst[j], lst[i]
for m in range(1, N):
a = m * (m + 1) // 2
b = (N - m) * (N + m + 1) // 2
if i < m <= j:
a = a - i + j
b = b - j + i
assert a == sum(lst[:m])
assert b == sum(lst[m:])
if sum(lst[:m]) == sum(lst[m:]):
found += 1
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j == N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) == N * (N + 1)
else:
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j != N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) != N * (N + 1)
return found
as you see i found criteria the indices have to fulfill in order for the sum to be correct:
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j == N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) == N * (N + 1)
i also found the direct formula to calculate the sum up to m and the one starting from m:
a = m * (m + 1) // 2
b = (N - m) * (N + m + 1) // 2
if i < m <= j:
a = a - i + j
b = b - j + i
all of that can can be calculated using some basic mathematics.
starting from that you can do some more maths and see that there are 2 cases to consider:
there is an m such that the sum of the original list [1, 2, 3, ..., m, m+1, ..., N] up to m equals the sum of the rest of the list (e.g. N = 20; m = 14). two cases again:
all the swaps that do not cross the m boundary are valid (there are comb(m, 2) + comb((N - m), 2)) of them.
when you split at m-1 you will find more swaps; this time you have to swap accross the m-1 boundary.
the m in that case is calculated from
m = - 1 + sqrt(1 + 2 * N * (N + 1)) / 2
the calculation for m in the first case is not an integer (i.e. 1 + 2 * N * (N + 1) is not a perfect square). the m to consider is then then the floor of the result of the formula above (i use int instead of math.floor). two cases again for the difference of the sum of the two splits:
the difference is even: there are more swaps that need to go over the m boundary.
the difference is odd: no additional swaps (swapping will always result in an even difference)
this is the code:
from math import sqrt, comb
def fast(N):
found = 0
arg = (1 + 2 * N * (N + 1))
sq = round(sqrt(arg))
if sq ** 2 == arg and sq & 1:
m = (-1 + sq) // 2
found += comb(m, 2) + comb((N - m), 2)
m -= 1
found += N - m - 1
else:
m = int((-1 + sqrt(arg)) // 2)
diff = ((m + 1 + N) * (N - m) - m * (m + 1)) // 2
if diff & 1 == 0:
found += N - m
return found

Creating a general function

I have a function defintion -
Now I have to create a function like this-
The problem is that since there are 4 combinations of (t,c)(where t is the feature and c is the class) which can occur that is (t,c) , (t',c) , (t,c') , (t', c'). So according to the values of t,c the function defintion will also change.
Is there any method apart from calculating a , b ,c ,d 4 times and then summing the function values?
The dataset looks like this-
feature file_frequency_M file_frequency_B
abc 2 5
my attempt-
dataset = pd.read_csv('.csv')
score = []
###list =[(t,c) ,(t,c0),(t0,c),(t0,c0)] ##representation of the combination of (t,c)
l=152+1394
for index, row in dataset.iterrows():
a = row['file_frequency_M']
b = row['file_frequency_B']
c = 152 - a
d = 1394 - b
temp_score = 0
tmp1 = 0
tmp2 = 0
tmp3 = 0
tmp4 = 0
for i in range(4):
if i == 0:
if a == 0:
tmp1 = 0
else:
tmp1 = log10(((a * l) / (a + c) * (a + b)))
temp_score += tmp1
if i == 1:
if b == 0:
tmp2 = 0
else:
tmp2 = log10(((b * l) / (b + d) * (b + a)))
temp_score += tmp2
if i == 2:
if c == 0:
tmp3 = 0
else:
tmp3 = log10(((c * l) / (c + a) * (c + d)))
temp_score += tmp3
if i == 3:
if d == 0:
tmp4 = 0
else:
tmp4 = log10(((d * l) / (d + b) * (d + c)))
temp_score += tmp4
score.append(temp_score)
np.savetxt("m.csv", score, delimiter=",")
You can save a lot of code repetition by creating a function representation of I(t,c):
import numpy as np
import pandas as pd
from math import log10
dataset = pd.read_csv('.csv')
score = []
###list =[(t,c) ,(t,c0),(t0,c),(t0,c0)] ##representation of the combination of (t,c)
l=152+1394
def I(a,b,c,n):
"""Returns I(t,c) = A*N/((A+C)*(A+B))"""
if a == 0:
return 0
return log10((a * n) / ((a + c) * (a + b)))
for index, row in dataset.iterrows():
a = row['file_frequency_M']
b = row['file_frequency_B']
c = 152 - a
d = 1394 - b
tmp1 = I(a,b,c,l)
tmp2 = I(b,a,d,l)
tmp3 = I(c,d,a,l)
tmp4 = I(d,c,b,l)
temp_score = sum(tmp1,tmp2,tmp3,tmp4)
score.append(temp_score)
np.savetxt("m.csv", score, delimiter=",")
Note: you appear to have an error in your code, according to the image of your function definition, it should be:
log10((a * n) / ((a + c) * (a + b)))
not
log10(((a * l) / (a + c) * (a + b)))
(note the parentheses placement).

Remove mixed-variable terms in SymPy series expansion

Consider two functions of SymPy symbols e and i:
from sympy import Symbol, expand, Order
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
This will produce
z = e**3*i**3 + e**3*i**2 + e**3*i + e**3 + e**2*i**3 + e**2*i**2 + e**2*i + e**2 + e*i**3 + e*i**2 + e*i + e + i**3 + i**2 + i + 1
However, assume that e and i are both small and we can neglect both terms that are order three or higher. Using Sympy’s series tool or simply adding an O-notation Order class can handle this:
In : z = expand(f*g + Order(i**3) + Order(e**3))
Out: 1 + i + i**2 + e + e*i + e*i**2 + e**2 + e**2*i + e**2*i**2 + O(i**3) + O(e**3)
Looks great. However, I am still left with mixed terms e**2 * i**2. Individual variables in these terms are less than the desired cut-off so SymPy keeps them. However, mathematically small²·small² = small⁴. Likewise, e·i² = small·small² = small³.
At least for my purposes, I want these mixed terms dropped. Adding a mixed Order does not produce the desired result (it seems to ignore the first two orders).
In : expand(f*g + Order(i**3) + Order(e**3) + Order((i**2)*(e**2)))
Out: 1 + i + i**2 + i**3 + e + e*i + e*i**2 + e*i**3 + e**2 + e**2*i + e**3 + e**3*i + O(e**2*i**2, e, i)
Question: Does SymPy have an easy system to quickly remove the n-th order terms, as well as terms that are (e^a)·(i^b) where a+b > n?
Messy Solution: I have found a way to solve this, but it is messy and potentially not general.
z = expand(f*g + Order((e**2)*i) + Order(e*(i**2)))
zz = expand(z.removeO() + Order(e**3) + Order(i**3))
produces
zz = 1 + i + i**2 + e + e*i + e**2 + O(i**3) + O(e**3)
which is exactly what I want. So to specify my question: Is there a way to do this in one step that can be generalized to any n? Also, my solution loses the big-O notation that indicates mixed-terms were lost. This is not needed but would be nice.
As you have a dual limit, you must specify both infinitesimal variables (e and i) in all Order objects, even if they don’t appear in the first argument.
The reason for this is that Order(expr) only automatically chooses those symbols as infinitesimal that actually appear in the expr and thus, e.g., O(e) is only for the limit e→0.
Now, Order objects with different limits don’t mix well, e.g.:
O(e*i)+O(e) == O(e*i) != O(e)+O(e*i) == O(e) # True
This leads to a mess where results depend on the order of addition, which is a good indicator that this is something to avoid.
This can be avoided by explicitly specifying the infinitesimal symbols (as addition arguments of Order), e.g.:
O(e*i)+O(e,e,i) == O(e,e,i)+O(e*i) == O(e,e,i) # True
I haven’t found a way to avoid going through all combinations of e and i manually, but this can be done by a simple iteration:
orders = sum( Order(e**a*i**(n-a),e,i) for a in range(n+1) )
expand(f*g+orders)
# 1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)
Without using Order you might try something simple like this:
>>> eq = expand(f*g) # as you defined
>>> def total_degree(e):
... x = Dummy()
... free = e.free_symbols
... if not free: return S.Zero
... for f in free:
... e = e.subs(f, x)
... return degree(e)
>>> eq.replace(lambda x: total_degree(x) > 2, lambda x: S.Zero)
e**2 + e*i + e + i**2 + i + 1
There is a way about it using Poly. I have made a function that keeps the O(...) term and another that does not (faster).
from sympy import Symbol, expand, Order, Poly
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
def neglect(expr, order=3):
z = Poly(expr)
# extract all terms and keep the lower order ones
d = z.as_dict()
d = {t: c for t,c in d.items() if sum(t) < order}
# Build resulting polynomial
return Poly(d, z.gens).as_expr()
def neglectO(expr, order=3):
# This one keeps O terms
z = Poly(expr)
# extract terms of higher "order"
d = z.as_dict()
large = {t: c for t,c in d.items() if sum(t) >= order}
for t in large: # Add each O(large monomial) to the expression
expr += Order(Poly({t:1},z.gens).as_expr(), *z.gens)
return expr
print(neglect(z))
print(neglectO(z))
This code prints the following:
e**2 + e*i + e + i**2 + i + 1
1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)

Montgomery Multiplication Algorithm on Python

I try Montgomery Multiplication Algorithm on Python 3.x. This algorithm pseudo-code is given below:
Input: Modulus N(n bit), gcd(n, 2) = 1, Multipler: A (n bit), Multiplicant: B (n bit)
Output: R = (A x B x 2 ^ (-n)) mod N
R = 0
for (i = 0; i < n; i++)
{
q = (R + A(i) * B) mod 2
R = (R + A(i) * B + q * N) / 2
}
print(R)
Python 3.x code that was written is given below:
#!/usr/bin/python3
N = 41
A = 13
B = 17
n = N.bit_length()
R = 0
for i in range(0, n):
q = int(R + (A & (1 << i) != 0) * B) % 2
R = int((R + (A & (1 << i) != 0) * B + q * N) / 2)
print("Result:", R % N)
But, the code isn't given correct result. What is the problem?
Thanks for answering.
When I run your (modified) code I get 31, and 31 appears to be the right answer.
According to your pseudocode, R should be
R = (A x B x 2 ^ (-n)) mod N
In your case that is
R = (13*17*2^(-6))%41
The interpretation of 2^(-6) when you are working mod 41 is to raise the mod 41 multiplicative inverse of 2 to the power 6, then take the result mod 41. In other words, 2^-6 = (2^-1)^6.
Since 2*21 = 42 = 1 (mod 41), 2^(-1) = 21 (mod 41). Thus:
R = (13*17*2^-6) (mod 41)
= (13*17*(2^-1)^6) (mod 41)
= (13*14*21^6) (mod 41)
= 18954312741 (mod 41)
= 31
which shows that the result is 31, the number returned by your code.
Thus your code is correct. If there is a clash between output and expectation, perhaps in this case the problem is one of expectation.

Categories