Repository navigation
Expand file tree
/
Copy pathintegers.py
More file actions
199 lines (158 loc) · 6.66 KB
/
Copy pathintegers.py
File metadata and controls
199 lines (158 loc) · 6.66 KB
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import math
import functools
import constraint
import intsystem
from sympy import *
from sympy.abc import x
import symflip
def waysToAddTo(numbers, addTo):
"""Input is numbers = list of numbers, addTo is number
output is list of lists of numbers
Each list in output sums to addTo"""
def waysToAddToImpl(numbers, addTo, numsSoFar):
for n in numbers:#try adding each number
nextList = numsSoFar + [n]
sumn = sum(nextList)
if sumn == addTo:
yield nextList
elif sumn < addTo:
lowerNumbers = [num for num in numbers if num <= n]#wlog, pieces decrease in size from first to last
yield from waysToAddToImpl(lowerNumbers, addTo, nextList)
else:#sumn > addTo:
yield from []#there has to be a better way to do this
return waysToAddToImpl(numbers, addTo, [])
def getPieces(d, intervals):
points = list(intervals.boundary)
newInterval = Union(*[Interval(x,x) for x in points])
if len(points) == 6:
newInterval = Union(newInterval, Interval(points[2], points[3]))
lowest, highest = int(points[0] * d), int(points[-1]*d + 1) #smallest and biggest pieces, working in units of 1/d
return [n for n in range(lowest, highest) if newInterval.contains(S(n)/d)] #get all pieces
def solve(m,s, d=None, intervals=None, pieces=None, spew=False, returnMatrix=False):
"""Creates matrix system that represents any possible solution of f(m,s) given denom d and assumption Q"""
#calculate some initial things we need
if intervals == None:
intervals = symflip.doit(m,s)
points = list(intervals.boundary)
Q = points[0] #bound predicted by interval theorem
if d == None:
d = lcm([fraction(f)[1] for f in points]) #denominator that we are going to use is lcm of denominators on ends of intervals
lowest, highest = int(points[0] * d), int(points[-1]*d + 1) #smallest and biggest pieces, working in units of 1/d
#lowest, highest, intervals = 1, d, Interval(0,1)
if pieces == None:
print("test: " + str([n for n in range(lowest, highest) if True]))
print("test: " + str(intervals.contains(S(13)/24)))
pieces = [n for n in range(lowest, highest) if intervals.contains(S(n)/d)] #get all pieces
if spew:
#pieces = getPieces(d, intervals) #this line would use only endpoints
print("pieces: " + str(pieces))
print("intervals: " + str(intervals))
muffins = list(waysToAddTo(pieces, d)) #all possible muffins
students = list(waysToAddTo(pieces, m*d//s)) #all possible students
if spew:
print("muffins, students:")
print(muffins)
print(students)
print("denominator:" + str(d))
# now create matrix
M = [] #each sublist is a row, each row is a piece size (except last two, to come later)
for p in pieces:#create row for each peice
M.append([])
for student in students:#for each possible student, add how many of each piece it has to its column (one column per student)
for i in range(len(pieces)):
rowForPiece = M[i]
howMany = student.count(pieces[i])#how many of this piece does this student have
rowForPiece.append(howMany) #add to the row
for muffin in muffins:#for each possible muffin, add how many of each piece it has to its column (one column per muffin)
for i in range(len(pieces)):
rowForPiece = M[i]
howMany = muffin.count(pieces[i])#how many of this piece does this muffin have
rowForPiece.append(-howMany) #add to the row, negative this time so that total adds to zero
#now add zeros on end of each row, as this is an equation with the right size of "=" being 0
for row in M:
row.append(0)
#not create two rows, one for total muffins, other for total students
#remember, left half of matrix is students, right half is muffins
M.append([1] * len(students) + [0] * len(muffins) + [s])#muffin total adds to m
M.append([0] * len(students) + [1] * len(muffins) + [m])#student total adds to s
#print("found matrix")
#return Matrix(M)
if returnMatrix:
yield Matrix(M)
else:
augmented = Matrix(M).rref()[0]
#print("rref done")
(mat, b) = deAugment(augmented)
#solutions = constraintSolveMat(m,s,mat,b)
solution = intsystem.solve(augmented, 0, m)
if solution == None:
return []
solutions = [solution]
#print("The denominator is: " + str(d))
for res in solutions:
#print("A solution:")
#res = [res['x'+str(i)] for i in range(len(res))]#constraints library returns a dictionary thing, this gets number from it
muffinList, studentList = [],[]
index = 0
for student in students:
if res[index] != 0:
#print(str(res[index]) + " students gets" + str(student) + '\\\\')
studentList.append((res[index], student))
index += 1
for muffin in muffins:
if res[index] != 0:
#print(str(res[index]) + " muffins split into " + str(muffin) + '\\\\')
muffinList.append((res[index], muffin))
index += 1
yield (muffinList, studentList)
def deAugment(aug):
mat = aug.copy()
b = aug.copy()
mat.col_del(-1)
for i in range(mat.cols):
b.col_del(0)
return (mat,b)
def constraintSolveMat(m, s, mat, b):
cols = mat.cols
varlist = ['x' + str(i) for i in range(cols)]
problem = constraint.Problem()
for var in varlist:
problem.addVariable(var, list(range(m + 1)))#for now, can be better TODO
for (row, bVal) in zip(mat.tolist(), list(b)):
nonzero = [i for i in range(len(row)) if row[i] != 0] #indices where the row is not zero
vars = [varlist[i] for i in nonzero]
test = makeTest(row, bVal, nonzero)
problem.addConstraint(test, vars)
return problem.getSolutionIter()
def makeTest(row, bVal, nonzero):
def test(*args):
total = 0
var = 0
for i in nonzero:
total += args[var] * row[i]
var += 1
return total == bVal
return test
def posIntSolve(numbers, addTo):
"""Find set of vectors of nonnegative integers that can be dot producted with numbers list to get addTo"""
def posIntSolveImpl(numbers, addTo, numsSoFar):
#numbers is [(number, info)] where info is anything you want
for value in numbers:#try adding each number
(n, info) = value
nextList = numsSoFar + [value]
sumn = sum([n for (n,info) in nextList])
if sumn == addTo:
yield nextList
elif sumn < addTo:
lowerNumbers = [(num, i) for (num, i) in numbers if num <= n]#wlog, pieces decrease in size from first to last
yield from posIntSolveImpl(lowerNumbers, addTo, nextList)
else:#sumn > addTo:
yield from []#there has to be a better way to do this
values = [(numbers[i], i) for i in range(len(numbers)) if numbers[i] != 0]
results = posIntSolveImpl(values, addTo, [])
indices = [[info for (n, info) in possibility] for possibility in results] #this is a list of lists of indices of numbers used
return [[possibility.count(n) for n in range(len(numbers))] for possibility in indices]
def lcm(args):
def lcm2(a, b):
return a * b // math.gcd(a, b)
return functools.reduce(lcm2, args)