If/else statement with a triplet that sum to a given value - python

I'm writing a sum up game where two players will take turns picking a random number in the range (1,9), no repeated number allowed. So I'm struggling at
If at any point exactly three of the player's numbers sum to 15, then that player has won.
If the first player picks [7, 2, 3, 5], he will win because 7+3+5 = 15
So my question is why doesn't the program stop when first_player has inputs == 15
I want to avoid importing any libs.

Instead of generating all permutations at each step, maintain a map of what each permutation sums to, then add two branches to each branch at each move.
Think of each entry as a set of bits, ie with each permutation you either include a given entry or not, eg if the numbers are [7, 3, 2] you might store [1, 0, 1] for the combination of the 7 and the 2.
You can make a hashmap of 101->9 etc and when someone adds a 3 to it you add an entry for 1010->9 and 1011->12. As soon as you see the target you know the game is over.
So the evolution of [7, 3, 2] would be
0->0
1->7
00->0
01->3
10->7
11->10
000->0
001->2
010->3
011->5
100->7
101->9
110->10
111->12

A more efficient way would be to find only those numbers whose sum is equal to the target that is 15.
entry = [7, 5, 1, 3]
def is_sum_15(nums):
res = []
search_numbers(nums, 3, 15, 0, [], res)
return len(res) != 0
def search_numbers(nums, k, n, index, path, res):
if k < 0 or n < 0:
return
if k == 0 and n == 0:
res.append(path)
for i in range(index, len(nums)):
search_numbers(nums, k-1, n-nums[i], i+1, path+[nums[i]], res)
print(is_sum_15(entry)) # True

An inefficient but easy way is to use itertools.permutations:
>>> entry = [7, 2, 3, 5]
>>> import itertools
>>> [sum(triplet) for triplet in itertools.permutations(entry, r=3) if sum(tr]
[12, 14, 12, 15, 14, 15, 12, 14, 12, 10, 14, 10, 12, 15, 12, 10, 15, 10, 14, 15, 14, 10, 15, 10]
>>> any(sum(triplet) == 15 for triplet in itertools.permutations(entry, r=3))
True
It's inefficient, because you would be trying all permutations every time entry gets expanded with a new number.

Related

how to print elements of a list whose sum is equal to a given number in python?

Consider a list of numbers from 0 to 50. I want to print all the combinations of elements from that list whose sum is equal to any given number say 41.
One combination is [4,5,7,9,16].I want to print other combinations like this.
You can use combinations(...) function from itertools
import itertools
nums = range(51)
for n in range(1, len(nums) + 1):
for p in itertools.combinations(nums, n):
if sum(p) == 41:
print(*p)
The following code is best used in a subroutine but it can be stand alone code.To change the average just change the number after the while loop. To change the amount of numbers change the amount of zeros in the lists. The random part can also be changed for the range of numbers.
def stats():
x =[0,0,0,0,0,0]
while mean(x) != 14.5:
x =[0,0,0,0,0,0]
for i in range(6):
a = random.randint(9,17)
x[i] = a
return x
The mean in this code is a separate subroutine that looks like this:
def mean(x):
y = sum(x)/6
return y
At the start of the code, you need to import the random library to allow the code to work properly. The only thing is that this code will only output one combination,such as:
stats()
[11, 12, 16, 17, 16, 15]
You could write a recursive generator to efficiently produce only the combinations of positive integers that sum up to the target number:
def sumSets(numbers,target):
if not target : yield []
if not numbers or target <= 0: return
yield from sumSets(numbers[1:],target)
yield from (numbers[:1]+ss for ss in sumSets(numbers[1:],target-numbers[0]))
nums = list(range(1,50,3))
for ss in sumSets(nums,41): print(ss)
[19, 22]
[16, 25]
[13, 28]
[10, 31]
[7, 34]
[4, 37]
[1, 40]
[1, 4, 7, 13, 16]
[1, 4, 7, 10, 19]
Note that, if you're looking for all combinations of numbers from 1 to 50 that sum up to 41, you're going to get a lot of them:
nums = list(range(1,51))
print(sum(1 for _ in sumSets(nums,41))) # 1260

Returning a list of the ordered elements with a conition in while loop structure

I am trying to solve a assignment where are 13 lights and starting from 1, light is turned off at every 5th light, when the count reaches 13, start from 1st item again. The function should return the order of lights turned off. In this case, for a list of 13 items, the return list would be [5, 10, 2, 8, 1, 9, 4, 13, 12, 3, 7, 11, 6]. Also, turned off lights would not count again.
So the way I was going to approach this problem was to have a list named turnedon, which is [1,2,3,4,5,6,7,8,9,10,11,12,13] and an empty list called orderoff and append to this list whenever a light gets turned off in the turnedon list. So while the turnedon is not empty, iterate through the turnedon list and append the light getting turned off and remove that turnedoff light from the turnedon list, if that makes sense. I cannot figure out what should go into the while loop though. Any idea would be really appreciated.
def orderoff():
n=13
turnedon=[]
for n in range(1,n+1):
turnedon.append(n)
orderoff=[]
while turneon !=[]:
This problem is equivalent to the well-known Josephus problem, in which n prisoners stand in a circle, and they are killed in a sequence where each time, the next person to be killed is k steps around the circle from the previous person; the steps are only counted over the remaining prisoners. A sample solution in Python can be found on the Rosetta code website, which I've adapted slightly below:
def josephus(n, k):
p = list(range(1, n+1))
i = 0
seq = []
while p:
i = (i+k-1) % len(p)
seq.append(p.pop(i))
return seq
Example:
>>> josephus(13, 5)
[5, 10, 2, 8, 1, 9, 4, 13, 12, 3, 7, 11, 6]
This works, but the results are different from yours:
>>> pos = 0
>>> result = []
>>> while len(result) < 13 :
... pos += 5
... pos %= 13
... if pos not in result :
... result.append(pos)
...
>>> result = [i+1 for i in result] # make it 1-based, not 0-based
>>> result
[6, 11, 3, 8, 13, 5, 10, 2, 7, 12, 4, 9, 1]
>>>
I think a more optimal solution would be to use a loop, add the displacement each time, and use modules to keep the number in range
def orderoff(lights_num,step):
turnd_off=[]
num =0
for i in range(max):
num =((num+step-1)%lights_num)+1
turnd_off.append(num)
return turnd_off
print(orderoff(13))

Insertion algorithm can not insert all nodes

I am newbie in Python. I want to insert new node into the current route to check whether it makes the route shorter or not. However my code doesn't run well, please show me the mistake. The steps are following:
1. Create random subtour (example: 0-2-0)
2. Get randomly the node which is not visited and check this node in each pair of nodes in current route. If the node satisfy the shorter requirement, we insert it into current node (example: 0-4-2-0).
3. Continue until all nodes inserted into the route.
import random
distMatrix = [
[100, 14, 20, 10, 35, 18, 5],
[6, 100, 7, 35, 17, 9, 24],
[8, 35, 100, 36, 27, 3, 15],
[21, 7, 12, 100, 7, 4, 26],
[33, 25, 6, 18, 100, 19, 11],
[6, 2, 22, 30, 9, 100, 8],
[24, 3, 12, 5,17, 16, 100],
]
def get_total_distance(route,d):
total = 0
for i in range (len(route)-1):
pre = route[i]
succ = route[i+1]
total += d[pre][succ]
return total
def insertion(d):
numNodes = len(d)
notVisited = list(range(1, numNodes))
first_random_node = random.choice(notVisited)
route = [0]
route.append(first_random_node)
notVisited.remove(first_random_node)
route.append(0) #create first subtour
print("1st",route)
location = 0
while len(notVisited) != 0:
for j in notVisited:
for i in range (len(route)-1):
pre = route[i]
succ = route[i+1]
check_route = d[pre][j] + d[j][succ]
current_distance = d[pre][succ]
if check_route <= current_distance:
print(j)
route.insert(i + 1, j)
notVisited.remove(j)
print("2nd", route)
return route
solution = insertion(distMatrix)
print("The solution for the route is:",solution)
print("The total distance is:", get_total_distance(solution,distMatrix))
As it is, your code doesn't run: there's a syntax error because you named a local function the same as a package you're trying to use: random. Inside your function random, you can no longer access the package of that name, because you redefined the name.
Once that is resolved, your code hangs with this output:
1st [0, 5, 0]
3
2nd [0, 3, 5, 0]
6
2nd [0, 6, 3, 5, 0]
You have a logic problem with this combination:
while len(notVisited) != 0:
print("WHILE", len(notVisited))
for j in notVisited:
....
if check_route <= current_distance:
print(j)
route.insert(i + 1, j)
notVisited.remove(j)
print("2nd", route)
Your only exit from the while loop is to remove everything from notVisited. There's no logical guarantee that will happen -- and when it doesn't, you're stuck in an infinite loop trying to find a shorter route that doesn't exist.

Custom list traversal and modification

I'm traversing a two-dimensional list (my representation of a matrix) in an unusual order: counterclockwise around the outside starting with the top-left element.
I need to do this more than once, but each time I do it, I'd like to do something different with the values I encounter. The first time, I want to note down the values so that I can modify them. (I can't modify them in place.) The second time, I want to traverse the outside of the matrix and modify the values of the matrix as I go, perhaps getting my new values from some generator.
Is there a way I can abstract this traversal to a function and still achieve my goals? I was thinking that this traverse-edge function could take a function and a matrix and apply the function to each element on the edge of the matrix. However, the problems with this are two-fold. If I do this, I don't think I can modify the matrix that's given as an argument, and I can't yield the values one by one because yield isn't a function.
Edit: I want to rotate a matrix counterclockwise (not 90 degrees) where one rotation moves, for example, the top-left element down one spot. To accomplish this, I'm rotating one "level" (or shell) of the matrix at a time. So if I'm rotating the outermost level, I want to traverse it once to build a list which I can shift to the left, then I want to traverse the outermost level again to assign it those new values which I calculated.
Just create 4 loops, one for each side of the array, that counts through the values of the index that changes for that side. For example, the first side, whose x index is always 0, could vary the y from 0 to n-2 (from the top-left corner to just shy of the bottom-left); repeat for the other sides.
I think there are two approaches you can take to solving your problem.
The first option is to create a function that returns an iterable of indexes into the matrix. Then you'd write your various passes over the matrix with for loops:
for i, j in matrix_border_index_gen(len(matrix), len(matrix[0])): # pass in dimensions
# do something with matrix[i][j]
The other option is to write a function that works more like map that applies a given function to each appropriate value of the matrix in turn. If you sometimes need to replace the current values with new ones, I'd suggest doing that all the time (the times when you don't want to replace the value, you can just have your function return the previous value):
def func(value):
# do stuff with value from matrix
return new_value # new_value can be the same value, if you don't want to change it
matrix_border_map(func, matrix) # replace each value on border of matrix with func(value)
I have added a few lines of python 3 code here. It has the mirror function and a spiral iterator (not sure, if that's what you meant). No doc strings (sorry). It is readable though. Change print statement for python 2.
EDIT : FIXED A BUG
class Matrix():
def __init__(self, rows=5, cols=5):
self.cells = [[None for c in range(cols)] for r in range(rows)]
def transpose(self):
self.cells = list(map(list, zip(*self.cells)))
def mirror(self):
for row in self.cells:
row.reverse()
def invert(self):
self.cells.reverse()
def rotate(self, clockwise=True):
self.transpose()
self.mirror() if clockwise else self.invert()
def iter_spiral(self, grid=None):
grid = grid or self.cells
next_grid = []
for cell in reversed(grid[0]):
yield cell
for row in grid[1:-1]:
yield row[0]
next_grid.append(row[1:-1])
if len(grid) > 1:
for cell in grid[-1]:
yield cell
for row in reversed(grid[1:-1]):
yield row[-1]
if next_grid:
for cell in self.iter_spiral(grid=next_grid):
yield cell
def show(self):
for row in self.cells:
print(row)
def test_matrix():
m = Matrix()
m.cells = [[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
print("We expect the spiral to be:", "4, 3, 2, 1, 5, 9, 13, 14, 15, 16, 12, 8, 7, 6, 10, 11", sep='\n')
print("What the iterator yields:")
for cell in m.iter_spiral():
print(cell, end=', ')
print("\nThe matrix looks like this:")
m.show()
print("Now this is how it looks rotated 90 deg clockwise")
m.rotate()
m.show()
print("Now we'll rotate it back")
m.rotate(clockwise=False)
m.show()
print("Now we'll transpose it")
m.transpose()
m.show()
print("Inverting the above")
m.invert()
m.show()
print("Mirroring the above")
m.mirror()
m.show()
if __name__ == '__main__':
test_matrix()
This is the output:
We expect the spiral to be:
4, 3, 2, 1, 5, 9, 13, 14, 15, 16, 12, 8, 7, 6, 10, 11
What the iterator yields:
4, 3, 2, 1, 5, 9, 13, 14, 15, 16, 12, 8, 7, 6, 10, 11,
The matrix looks like this:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
[13, 14, 15, 16]
Now this is how it looks rotated 90 deg clockwise
[13, 9, 5, 1]
[14, 10, 6, 2]
[15, 11, 7, 3]
[16, 12, 8, 4]
Now we'll rotate it back
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
[13, 14, 15, 16]
Now we'll transpose it
[1, 5, 9, 13]
[2, 6, 10, 14]
[3, 7, 11, 15]
[4, 8, 12, 16]
Inverting the above
[4, 8, 12, 16]
[3, 7, 11, 15]
[2, 6, 10, 14]
[1, 5, 9, 13]
Mirroring the above
[16, 12, 8, 4]
[15, 11, 7, 3]
[14, 10, 6, 2]
[13, 9, 5, 1]
I would go with generator functions. They can be used to create iterators over which we can iterate. An Example of a generator function -
def genfunc():
i = 0
while i < 10:
yield i
i = i + 1
>>> for x in genfunc():
... print(x)
...
0
1
2
3
4
5
6
7
8
9
When calling the generator function, it returns a generator object -
>>> genfunc()
<generator object genfunc at 0x00553AD0>
It does not start going over the function at that point. When you start iterating over the generator object, calling for its first element, it starts going over the function, untill it reaches the first yield statement, and at that point it returns the value (in above case, it returns value of i) . And it also saves the state of the function at that point (that is it saves at what point the execution was when the value was yielded, what were the values for the variables in the local namespace, etc).
Then when it tries to get the next value, again execution starts from where it stopped last time, till it again yield another value. And this continues on.

Problem with quicksort and python

I'm writing a python program that plays poker for a class, and I need to sort a list of five card hands. I have a function called wins(), which takes two hands and returns True if the first one beats the second one, False if it doesn't. I wrote an implementation of quicksort to sort the list of hands, and I noticed that it was taking much longer than expected, so I programmed it to print the length of each list it was sorting. The function looks like this:
def sort(l):
if len(l) <= 1:
return l
print len(l)
pivot = choice(l)
l.remove(pivot)
left = []
right = []
for i in l:
if wins(i, pivot) == True:
right.append(i)
else:
left.append(i)
return sort(left) + [pivot] + sort(right)
and when I had it sort 64 hands, it printed:
64,
53,
39,
26,
25,
24,
23,
22,
21,
20,
19,
18,
17,
16,
15,
14,
13,
12,
11,
10,
9,
8,
7,
6,
5,
4,
3,
2,
12,
7,
3,
2,
3,
2,
4,
3,
2,
13,
9,
6,
2,
3,
2,
2,
3,
10,
8,
2,
5,
4,
3,
2. Notice the consecutive sequence in the middle? I can't figure out why it does that, but it's causing quicksort to behave like an O(n^2). It doesn't make sense for it to choose the strongest hand as a pivot on every iteration, but that's what seems to be happening. What am I overlooking?
Edited answer after comments:
Could you try the following code and share your results. It introduces an equivalence class to reduce the population in the "lose or tie" (or "smaller than or equal to") group while forming an answer via recursion.
# modified quicksort with equivalence class.
def sort(l):
if len(l) <= 1:
return l
print len(l)
pivot = choice(l)
l.remove(pivot)
left = []
right = []
# I added an equivalence class
pivotEquivalence = [pivot]
# and an equivalence partitioning
# to reduce number of elements
# in the upcoming recursive calls
for i in l:
if wins(i, pivot) == True:
right.append(i)
elif wins(pivot,i) == True:
left.append(i)
else
pivotEquivalence.append(i)
return sort(left) + pivotEquivalence + sort(right)
======
Instead of choosing the pivot randomly, try taking the middle indexed element. on average it will guarantee the O(N log N).
Also for O notation, you need to make many simulations to collect empirical data. Just one example might be very misleading.
Try to print the pivot, and it's index as well. Shuffle your list by random.shuffle(list) and try again to see the distribution. Just ensure system time seeding as well.
Could you send post the complete code, and your data for community to repeat the problem?
Sincerely,
Umut

Categories

Resources