Let list l consists of n elements, where
each element should be either 0 or a positive integer less than or equal to r, and
the sum of list should be equal to m
Example:
Given n = 5, r = 4, m = 10
l = [4, 3, 2, 0, 1]
It is easy to fulfill rule(1), but I wonder if there is any good idea/algo to fulfill both rules?
Here's a simple brute force solution. Basically, you want to generate samples where random integers less than r are equally likely. Samples that do not meet the criteria (sum to m) are rejected.
import numpy as np
def rand_with_rules(n, r, m):
if n*(r-1) < m:
raise ValueError
while True:
l = np.random.randint(0, r, size=(n))
if l.sum() == m:
return l
Note that the rejection of samples will necessarily bias your "random" numbers. Because of your constraints, you can't have a purely set of random and certain will tend to be over or underrepresented.
Take, for example, the case n, r, m = 2, 3, 4. The only series that matches this criteria is (2, 2), so the likelihood of drawing 2 is 100% and 0% for other values.
In essence, this solution states that you have no prior knowledge of which integers are most likely. However, through the constraints, your posterior knowledge of the numbers will almost never be truly uniform.
There's probably a clever analytic solution for the distribution of integers given this constraint, which you could use to generate samples. But I don't know what it is!
Without validation that all rules can be meet at once, I suggest following solution
import random
def dis(n,m,r):
l = [0] * n
for inc in range(1,m):
l.sort()
for ind_less_r, less_r in enumerate(l):
if less_r > r:
break
l[random.randint(0,ind_less_r-1)] += 1
random.shuffle(l)
return l
for i in range(1,10):
print dis(10,50,9)
result is
[7, 7, 6, 1, 6, 3, 5, 4, 4, 6]
[5, 6, 7, 5, 4, 7, 4, 4, 4, 3]
[4, 3, 2, 4, 7, 7, 5, 7, 5, 5]
[4, 4, 5, 6, 4, 6, 6, 4, 5, 5]
[6, 6, 4, 6, 5, 6, 2, 5, 4, 5]
[2, 8, 4, 2, 6, 5, 4, 4, 6, 8]
[6, 6, 3, 4, 5, 5, 5, 5, 6, 4]
[6, 4, 5, 6, 7, 3, 1, 5, 6, 6]
[4, 5, 4, 7, 6, 6, 3, 2, 6, 6]
A reasonable interpretation would be to (i) find and count all unique lists satisfying the rules; and (ii) pick one of those lists at random with equal probability. The problem with this method is that it is complicated to code the list of lists.
A simpler way to do it (less code) is to expose each correct list to the same probability of being picked. That algorithm is:
check that m<=nr If not, return None or raise an error.
Loop: repeatedly generate lists with random numbers in [0,r]. Break the loop and return the first list of sum m.
Note: The tradeoff here for less code is potentially longer execution time. if r is large, or m is an improbable sum, this may take a while. We can mitigate this a little by checking the limits where the answer can only be zeros or r-1's.
from numpy.random import randint
def known_sum_random_list(size,limitint,knownsum):
if knownsum>size*(limitint-1):
return None
if knownsum==size*(limitint-1):
return size*[limitint-1]
s=0
l = size*[0]
while (s!=knownsum):
l = randint(0,limitint,size)
s = sum(l)
return l
for t in xrange(10):
print known_sum_random_list(5,4,10)
output:
[3 2 1 2 2]
[1 1 2 3 3]
[3 0 3 1 3]
[3 2 0 3 2]
[2 2 0 3 3]
[1 3 2 3 1]
[3 3 0 3 1]
[2 0 2 3 3]
[3 1 2 3 1]
[3 2 0 3 2]
Since you responded in comments that it can have many 0s and numbers can repeat, I infer that it need not be all that random. With that in mind, here is a basic solution without any loops or includes. It assumes n, r, and m have valid values and types. But such checks are simple enough to add, I'll edit them in upon request.
def create_list(n, r, m):
output = [r] * (m/r)
remainder = m - r * len(output)
if remainder != 0:
output += [m - r * len(output)]
output += [0] * (n - len(output))
return output
output = create_list(5, 4, 10) # gives [4, 4, 2, 0, 0]
output = create_list(5, 2, 10) # gives [2, 2, 2, 2, 2]
P.S. - request was for values less than r, but example showed values equaling r, so this is going by the example
Related
Shift the items in the given array, by some number of times, as shown in the below examples;
array = [1, 2 ,3 , 4, 5, 6]
k1 = 2
k2 = -3
k3 = 20
test1:
cirShift(array, k1)
Result: [5, 6, 1, 2, 3, 4]
test2:
cirShift(array, k2)
Result: [4, 5, 6, 1, 2, 3]
test3:
cirShift(array, k3)
Result: [5, 6, 1, 2, 3, 4]
I have used the below to achieve the right-rotate a list by k positions;
def rightRotateByOne(A):
Fin= A[-1]
for i in reversed(range(len(A) - 1)):
A[i + 1] = A[i]
A[0] = Fin
def rightRotate(A, k):
if k < 0 or k >= len(A):
return
for i in range(k):
rightRotateByOne(A)
if __name__ == '__main__':
A = [1, 2, 3, 4, 5, 6, 7]
k = 3
rightRotate(A, k)
print(A)
As of now, able to obtain results for test1 but would like to achieve the test2 and test3
Even easier, split the array in half given the boundary, swap and glue back:
def cirShift(a, shift):
if not shift or not a:
return a
return a[-shift%len(a):] + a[:-shift%len(a)]
Courtesy of #KellyBundy, a short-circut one-liner:
def cirShift(a, shift):
return a and a[-shift%len(a):] + a[:-shift%len(a)]
I think this question may be an exercise in self learning ('how to do X using just python'), so my answer is auxiliary, but you can always use np.roll():
#test 1
import numpy as np
a = [1, 2 ,3, 4, 5, 6]
np.roll(a, 2)
gives output
[5, 6, 1, 2, 3, 4]
and
#test 2
np.roll(a, -2)
gives output
[3, 4, 5, 6, 1, 2]
Even if you give a number that is larger than the array size, it handles the overflow:
#test 3
np.roll(a, 10)
gives output
[3, 4, 5, 6, 1, 2]
Rolling also works in multiple dimension arrays and across specified axes, which is pretty neat.
def shift(l, shift_t):
r = [*l]
for n in range(abs(shift_t)):
if shift_t < 0:
r = r[1:] + [r[0]]
else:
r = [r[-1]] + r[:-1]
return r
The key is to take one item of the list and place it on the opposite side, which is essentially all that shifting is doing. If you shift negative, you put the first one at the end, and if you shift positive, you put the last one at the beginning.
i/p 1:
test_list = [1, 1, 3, 4, 4, 4, 5,6, 6, 7, 8, 8, 6]
o/p
[3, 5, 7, 6]
Exp: Since (1 1), (4 4 4) (6 6) (8 8) are in consecutive occurrence so resultant list has no addition of 6 but for last occurrence where 8, 6 are not in multiple consecutive occurrence so 6 is valid
in last iteration
i/p 2:
test_list = [1, 1, 3, 4, 4, 4, 5,4,6, 6, 7, 8, 8, 6]
o/p
[3, 5,4, 7, 6]
** like wise for 2nd input 4,4,4 is not valid but 5,4 is valid
Any suggestion for the expected o/p?
(I am looking for bit elaborated algorithm)
You can use itertools.groupby to group adjacent identical values, then only keep values that have group length of 1.
>>> from itertools import groupby
>>> test_list = [1, 1, 3, 4, 4, 4, 5,6, 6, 7, 8, 8, 6]
>>> [k for k, g in groupby(test_list) if len(list(g)) == 1]
[3, 5, 7, 6]
>>> test_list = [1, 1, 3, 4, 4, 4, 5,4,6, 6, 7, 8, 8, 6]
>>> [k for k, g in groupby(test_list) if len(list(g)) == 1]
[3, 5, 4, 7, 6]
First of all, you need to know that increasing i in your for loop does not change the value of i.
You can check it by runin this code:
for i in range(5):
print(i)
i = 2
This code will print 0 1 2 3 4 not 0 2 2 2 2 as you might think.
Going back to your question. I would use groupby from itertools, but since you specified you don't want to use it, I would do something like this:
if test_list[0] != test_list[1]: # <-- check if first element should belong to result
res_list.append(test_list[0])
for i in range(len(test_list[1:-1])): # Here we use input list, but without first and last element.
if test_list[i+1] == test_list[i+2] or test_list[i+1] == test_list[i]:
continue
else:
res_list.append(test_list[i+1])
if test_list[-2] != test_list[-1]: # <-- check if last element should belong to result
res_list.append(test_list[-1])
I am trying to find a way to get as many solutions as possible for a 3x3 matrix to have all rows and columns add up to the same number. It must use the numbers 1-9. I figured out that I need to use 1 big, med, and small number in each row for it to work.
Ex:
2 4 9 = 15
6 8 1 = 15
7 3 5 = 15
= = =
15 15
I have a dict with the usable numbers grouped by size and a matrix with the 3 biggest numbers each of which are in a separate row because nothing would add up to them if they were in the same row.
nums = {
"small" : [1, 2, 3],
"med" : [4, 5, 6],
"big" : [7, 8, 9]
}
m = [
[0, 0, 9],
[0, 8, 0],
[7, 0, 0]
]
What would be the best way to find all possible solutions to this?
There are two problems to solve:
Generate new possible solutions
Verify that the solution is valid
Step one is easy; python includes a permutation function which will generate every single arrangement of numbers for you. Then you need to verify that the sums all agree. We can simplify that by using #JohanC's observation that each row and column must sum to 15.
from itertools import permutations
def all_sums():
# Generate all possible grids
r = range(1, 10)
grids = permutations(r)
# Only keep grids that are valid solutions
solutions = [g for g in grids if _all_sums_are_15(g)]
return solutions
def _all_sums_are_15(grid):
"""Check that each row and column of the grid sums to 15"""
return (_sum_is_15(grid, 0, 1, 2) and
_sum_is_15(grid, 3, 4, 5) and
_sum_is_15(grid, 6, 7, 8) and
_sum_is_15(grid, 0, 3, 6) and
_sum_is_15(grid, 1, 4, 7) and
_sum_is_15(grid, 2, 5, 8))
def _sum_is_15(grid, a, b, c):
"""Determine if the given 3 cells in the grid sum up to 15"""
sum_ = grid[a] + grid[b] + grid[c]
return sum_ == 15
if __name__ == '__main__':
for s in all_sums():
print(s)
First, note that the sum of each row/column must necessarily be 15, because the sum of the 3 rows together must be equal to the sum of the numbers from 1 to 9, so 45.
Here is a way to generate all 72 solutions using Z3, an open source SAT/SMT solver. Note that Z3 is a powerful solver for this kind of combinatorial problems, and probably a bit overkill for this specific one. But it can be used as an example of how such combinatorial problems can be handled, also much trickier ones. See e.g. this long list of examples.
from z3 import *
# get 9 integer variables for the matrix elements
M = [[Int(f"m{i}{j}") for j in range(3)] for i in range(3)]
# create a Z3 solver instance
s = Solver()
# all numbers must be between 1 and 9
s.add([And(M[i][j] >= 1, M[i][j] <= 9) for i in range(3) for j in range(3)])
# all the rows must sum to 15
s.add([And([Sum([M[i][j] for j in range(3)]) == 15]) for i in range(3)])
# all the columns must sum to 15
s.add([And([Sum([M[i][j] for i in range(3)]) == 15]) for j in range(3)])
# all 9 numbers must be distinct
s.add(Distinct([M[i][j] for i in range(3) for j in range(3)]))
res = s.check()
num_solutions = 0
while res == sat:
num_solutions += 1
m = s.model()
print(num_solutions, ":", [[m[M[i][j]] for j in range(3)] for i in range(3)])
# add a new condition that at least one of the elements must be different to the current solution
s.add(Or([m[M[i][j]].as_long() != M[i][j] for i in range(3) for j in range(3)]))
res = s.check()
Output:
1 : [[3, 4, 8], [5, 9, 1], [7, 2, 6]]
2 : [[5, 7, 3], [9, 2, 4], [1, 6, 8]]
3 : [[6, 8, 1], [7, 3, 5], [2, 4, 9]]
...
72 : [[7, 5, 3], [6, 1, 8], [2, 9, 4]]
All solutions are equivalent to each other. You can permute rows and or columns of a solution to get another. And you can mirror a solution. 3! row permutations times 3! column permutations times 2 for the mirroring, 72 altogether.
Say we have a number of elements E and a number of sets S.
We need to assign elements to sets so that:
All sets roughly contain the same number of elements (minimum
difference in set size between the smallest and largest set)
The number of elements per set should be as small as possible.
Each element needs to be assigned to at least a minimum % of sets of the total. This % is specified for each element (this
implies that elements are course be assigned to multiple sets
accordingly)
Note that (1) and (2) are problem objectives, and in some instances there is a tradeoff between them. I'm effectively looking for a mathematical formulation / solution that parameterizes this tradeoff. Meanwhile (3) is just a problem constraint.
How do we find an optimal assignment? Does this problem have a name in the literature? In case it matters, I'm specifically looking for a solution in Python.
As an example, say we have 3 sets and 10 elements, each of them specifying the min. fraction of sets as follows:
0 97.844356
1 48.006223
2 99.772135
3 16.899074
4 0.111023
5 1.028894
6 5.315590
7 100.000000
8 99.838698
9 93.323315
You could just rotate infinitely over the sets in order to determine the next set to assign to. Then for each element calculate how many sets it should be assigned to and then do the assignment accordingly:
from itertools import cycle
from math import ceil
elems = [
[0, 97.844356],
[1, 48.006223],
[2, 99.772135],
[3, 16.899074],
[4, 0.111023],
[5, 1.028894],
[6, 5.315590],
[7, 100.000000],
[8, 99.838698],
[9, 93.323315]
]
def assign(elements, n):
sets = [[] for _ in range(n)]
gen = (e for e, p in elements for _ in range(ceil(p*n/100)))
for s, e in zip(cycle(sets), gen):
s.append(e)
return sets
print(assign(elems, 3))
Output:
[[0, 1, 2, 4, 7, 8, 9], [0, 1, 2, 5, 7, 8, 9], [0, 2, 3, 6, 7, 8, 9]]
In above cycle is used to iterate infinitely over the target sets. gen is a generator that returns the minimum amount of elements to add based on the probabilities:
>>> n = 3
>>> gen = (e for e, p in elems for _ in range(ceil(p*n/100)))
>>> list(gen)
[0, 0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9]
Finally zip is used to generate (target set, element) tuples which are then assigned within a loop.
I have numbered my tic-tac-toe game cells as:
0 1 2
3 4 5
6 7 8
Now, I want to create a formula or function (preferable formula) in Python such that when passed with a cell number, it gives a list of all cells of that row.
I created a formula for solving the same for the columns: i, (i + 3) % 9 and (i + 6) % 9.
Similarly, I want a general expression for the rows. Any ideas?
You can use a similar expression to create a row list. The code below creates both row and column lists using a pair of similar functions that both use the built-in divmod() function to convert the grid index into (X, Y) coordinates and a helper function xy_to_i() to convert such coordinates back into a grid index.
#!/usr/bin/env python
grid = [range(i, i+3) for i in range(0, 9, 3)]
for row in grid:
print row
print
def xy_to_i(x, y): return 3 * (y % 3) + (x % 3)
def samerow(i):
y, x = divmod(i, 3)
return [xy_to_i(x + j, y) for j in (0, 1, 2)]
def samecol(i):
y, x = divmod(i, 3)
return [xy_to_i(x, y + j) for j in (0, 1, 2)]
for i in range(9):
print i, samerow(i), samecol(i)
output
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
0 [0, 1, 2] [0, 3, 6]
1 [1, 2, 0] [1, 4, 7]
2 [2, 0, 1] [2, 5, 8]
3 [3, 4, 5] [3, 6, 0]
4 [4, 5, 3] [4, 7, 1]
5 [5, 3, 4] [5, 8, 2]
6 [6, 7, 8] [6, 0, 3]
7 [7, 8, 6] [7, 1, 4]
8 [8, 6, 7] [8, 2, 5]
Now see if you can make a function that handles the diagonals properly. :)
For a square n x n grid, the indexes of the cells in the row containing index i are returned by:
def row_indexes(i, n):
"""
Return the indexes or cells in the row containing index i for a
n x n grid.
"""
rowi = n * (i // 3)
return list(range(rowi,rowi+n))
Similarly for columns:
def col_indexes(i, n):
"""
Return the indexes or cells in the column containing index i for a
n x n grid.
"""
coli = i % n
return list(range(coli, n*n, n))
(omit the list() if you're using Python 2, since range will already return you a list).
For example,
>>> row_indexes(5,3)
[3, 4, 5]
>>> col_indexes(5,3)
[2, 5, 8]