Reduce list of tuples - python

As part of my journey into learning python I am implementing Bulls and Cows.
I have a working implementation that uses list comprehension but I figured it might be a nice solution to solve this using a generator and reduce()-ing the final result.
So I have my generator:
def bullsandcows(given, number):
for i in range(given.__len__()):
if given[i] == number[i]:
yield (given[i], None)
elif given[i] in number:
yield (None, given[i])
And my reduce implementation:
(bulls, cows) = reduce(\
lambda (bull, cow), (b, c): \
(bull + 1, cow + 1), bullsandcows(given, number), (0, 0))
Where given is the user input and number is the randomly generated number for the user to guess.
As you can see, this is not exactly a working implementation, this will just return the counts of the yielded tuples.
What I need is a replacement for (bull + 1, cow + 1), I have no idea how to construct this.
number is a randomly generated number, say: 1234
given is entered by the user, say: 8241
The result of bullsandcows(given, number) would be: [('2', None), (None, '4'), (None, '1']
The result of the reduce should be: (1, 2), which is the count of all non-None values of the first element and count of all non-None values of the second element

If I understood the process correctly, you want to count what bulls are not None, and how many cows are not None:
reduce(lambda (bcount, ccount), (b, c): (bcount + (b is not None), ccount + (c is not None)),
bullsandcows(given, number), (0, 0))
This increments a counter only if the bull or cow value is not None. The test produces a boolean, which is a subclass of int with False == 0 and True == 1; summing an integer and a boolean results in another integer.
Since you are feeding it non-empty strings, you could simplify it to:
reduce(lambda (bcount, ccount), (b, c): (bcount + bool(b), ccount + bool(c)),
bullsandcows(given, number), (0, 0))
I'd rewrite bullsandcows() to:
def bullsandcows(given, number):
given, number = map(str, (given, number))
for g, n in zip(given, number):
if g == n:
yield (g, None)
elif g in number:
yield (None, g)
e.g. use zip() to pair up the digits of given and number.
Demo:
>>> def bullsandcows(given, number):
... given, number = map(str, (given, number))
... for g, n in zip(given, number):
... if g == n:
... yield (g, None)
... elif g in number:
... yield (None, g)
...
>>> given, number = 8241, 1234
>>> list(bullsandcows(given, number))
[('2', None), (None, '4'), (None, '1')]
>>> reduce(lambda (bcount, ccount), (b, c): (bcount + bool(b), ccount + bool(c)),
... bullsandcows(given, number), (0, 0))
(1, 2)
Note that unpacking in function arguments was removed from Python 3 and the reduce() built-in has been delegated to library function; your code is decidedly Python 2 only.
To make it work in Python 3 you need to import functools.reduce() and adjust the lambda to not use unpacking:
from functools import reduce
reduce(lambda counts, bc: (counts[0] + bool(bc[0]), counts[1] + bool(bc[1])),
bullsandcows(given, number), (0, 0))

Related

Minimum moves to reach k

Given two numbers m and n, in one move you can get two new pairs:
m+n, n
m, n+m
Let's intially set m = n = 1 find the minimum number of moves so that at least one of the numbers equals k
it's guaranteed there's a solution (i.e. there exist a sequence of moves that leads to k)
For example:
given k = 5
the minimum number of moves so that m or n is equal to k is 3
1, 1
1, 2
3, 2
3, 5
Total of 3 moves.
I have come up with a solution using recursion in python, but it doesn't seem to work on big number (i.e 10^6)
def calc(m, n, k):
if n > k or m > k:
return 10**6
elif n == k or m == k:
return 0
else:
return min(1+calc(m+n, n, k), 1+calc(m, m+n, k))
k = int(input())
print(calc(1, 1, k))
How can I improve the performance so it works for big numbers?
Non-Recursive Algorithm based on Priority Queue (using Heap)
State: (sum_, m, n, path)
sum_ is current sum (i.e. m + n)
m and n are the first and second numbers
path is the sequence of (m, n) pairs to get to the current sum
In each step there are two possible moves
Replace first number by the sum
Replace second number by the sum
Thus each state generates two new states. States are prioritized by:
moves: states with a lower number of have higher priority
sum: States with higher sums have higher priority
We use a Priority Queue (Heap in this case) to process states by priority.
Code
from heapq import heappush, heappop
def calc1(k):
if k < 1:
return None, None # No solution
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves, [(m, n)]
h = [] # Priority queue (heap)
path = [(m, n)]
sum_ = m + n
# Python's heapq acts as a min queue.
# We can order thing by max by using -value rather than value
# Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
heappush(h, (moves+1, -sum_, sum_, n, path))
heappush(h, (moves+1, -sum_, m, sum_, path))
while h:
# Get state with lowest sum
moves, sum_, m, n, path = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves, path # Found solution
if sum_ < k:
sum_ = m + n # new sum
# Replace first number with sum
heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
# Replace second number with sum
heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
# else:
# so just continues since sum_ > k
# Exhausted all options, so no solution
return None, None
Test
Test Code
for k in [5, 100, 1000]:
moves, path = calc1(k)
print(f'k: {k}, Moves: {moves}, Path: {path}')
Output
k: 5, Moves: 3, Path: [(1, 1), (2, 3), (2, 5)]
k: 100, Moves: 10, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(8, 19), (27, 19), (27, 46), (27, 73), (27, 100)]
k: 1000, Moves: 15, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(19, 11), (19, 30), (49, 30), (79, 30), (79, 109),
(188, 109), (297, 109), (297, 406), (297, 703), (297, 1000)]
Performance Improvement
Following two adjustments to improve performance
Not including path just number of steps (providing 3X speedup for k = 10,000
Not using symmetric pairs (provided 2x additional with k = 10, 000
By symmetric pairs, mean pairs of m, n which are the same forward and backwards, such as (1, 2) and (2, 1).
We don't need to branch on both of these since they will provide the same solution step count.
Improved Code
from heapq import heappush, heappop
def calc(k):
if k < 1:
return None, None
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves
h = [] # Priority queue (heap)
sum_ = m + n
heappush(h, (moves+1, -sum_, sum_, n))
while h:
moves, sum_, m, n = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves
if sum_ < k:
sum_ = m + n
steps = [(sum_, n), (m, sum_)]
heappush(h, (moves+1, -sum_, *steps[0]))
if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
heappush(h, (moves+1, -sum_, *steps[1]))
Performance
Tested up to k = 100, 000 which took ~2 minutes.
Update
Converted solution by #גלעדברקן from JavaScript to Python to test
def g(m, n, memo):
key = (m, n)
if key in memo:
return memo[key]
if m == 1 or n == 1:
memo[key] = max(m, n) - 1
elif m == 0 or n == 0:
memo[key] = float("inf")
elif m > n:
memo[key] = (m // n) + g(m % n, n, memo)
else:
memo[key] = (n // m) + g(m, n % m, memo)
return memo[key]
def f(k, memo={}):
if k == 1:
return 0
return min(g(k, n, memo) for n in range((k // 2) + 1))
Performance of #גלעדברקן Code
Completed 100K in ~1 second
This is 120X faster than my above heap based solution.
This is an interesting problem in number theory, including linear Diophantine equations. Since there are solutions available on line, I gather that you want help in deriving the algorithm yourself.
Restate the problem: you start with two numbers characterized as 1*m+0*n, 0*m+1*n. Use the shorthand (1, 0) and (0, 1). You are looking for the shortest path to any solution to the linear Diophantine equation
a*m + b*n = k
where (a, b) is reached from starting values (1, 1) a.k.a. ( (1, 0), (0, 1) ).
So ... starting from (1, 1), how can you characterize the paths you reach from various permutations of the binary enhancement. At each step, you have two choices: a += b or b += a. Your existing algorithm already recognizes this binary search tree.
These graph transitions -- edges along a lattice -- can be characterized, in terms of which (a, b) pairs you can reach on a given step. Is that enough of a hint to move you along? That characterization is the key to converting this problem into something close to a direct computation.
We can do much better than the queue even with brute force, trying each possible n when setting m to k. Here's JavaScript code, very close to Python syntax:
function g(m, n, memo){
const key = m + ',' + n;
if (memo[key])
return memo[key];
if (m == 1 || n == 1)
return Math.max(m, n) - 1;
if (m == 0 || n == 0)
return Infinity;
let answer;
if (m > n)
answer = Math.floor(m / n) + g(m % n, n, memo);
else
answer = Math.floor(n / m) + g(m, n % m, memo);
memo[key] = answer;
return answer;
}
function f(k, memo={}){
if (k == 1)
return 0;
let best = Infinity;
for (let n=1; n<=Math.floor(k/2); n++)
best = Math.min(best, g(k, n, memo));
return best;
}
var memo = {};
var ks = [1, 2, 5, 6, 10, 100, 1000, 100000];
for (let k of ks)
console.log(`${ k }: ${ f(k, memo) }`);
By default, in Python, for a recursive function the recursion limit is set to 10^4. You can change it using sys module:
import sys
sys.setrecursionlimit(10**6)

Complex operations with Python (pygame.math.Vector2)

I'm learning Python and came across a complex expression that derives from pygame.Vector2:
import pygame
x = pygame.math.Vector2 (1,2)
b = x * 5 - (1, 2)
print (x)
print (b)
Result:
[1,2]
[4,8]
In the above case, the same x * 5 operation is performed both for the 1 and 2 values of Vector2, resulting in (5, 10) respectively; and then both results are subtracted from the tuple (1, 2), resulting in [4,8]
However if I do assign a simple tuple to x: x = (1, 2), instead of Vector2, I get the error:
TypeError: unsupported operand type (s) for -: 'tuple' and 'tuple'
My question is: At what times in Python I can perform these complex operations?
Can do something like (see comments too):
x = (1,2) # create a `tuple`
b = map(lambda x: x * 5,x) # do a `map` object for multiplying 5 to all of them
print(x) # print the `tuple`
t=iter((1,2)) # do an generator object using `iter`, so able to use `next` to access every next element
print(tuple(map(lambda x: x-next(t),b))) # do the `next` and another `map`, to subtract as you wanted
Best thing is still to create a class:
from __future__ import division
class MyClass:
def __init__(self,t):
self.t=t
def __mul__(self,other):
return MyClass(tuple(map(lambda x: x*other,self.t)))
def __truediv__(self,other):
return MyClass(tuple(map(lambda x: x/other,self.t)))
def __sub__(self,other):
gen=iter(other)
return MyClass(tuple(map(lambda x: x-next(gen),self.t)))
def __add__(self,other):
gen=iter(other)
return MyClass(tuple(map(lambda x: x+next(gen),self.t)))
def __repr__(self):
return str(tuple(self.t))
Then now can do anything:
x = MyClass((1,2))
b = x*5
print(b)
print(b-(1,2))
Output:
(5, 10)
(4, 8)
Also can do addition:
x = MyClass((1,2))
b = x*5
print(b)
print(b-(1,2)+(3,4))
Output:
(5, 10)
(7, 12)
Also division:
x = MyClass((1,2))
b = x*5
print(b)
print((b-(1,2)+(3,4))/2)
Output:
(5, 10)
(3.5, 6.0)

Return the subset of a list with the largest sum up to n recursively

def pack(L, n):
'''Return the subset of L with the largest sum up to n
>>> s = [4,1,3,5]
>>> pack(s, 7)
{3, 4}
>>> pack(s, 6)
{1, 5}
>>> pack(s, 11)
{1, 4, 5}
'''
I'm asked to code this. It takes in a list and an integer and returns the best combination to get that integer less than or equal to.
I used a helper function that takes in the sum, but it's not correct since I don't know how I could replace a number while in recursion.
# doesn't work as intended
def pack_helper(L, n, sum=0):
'''Return the subset of L with the largest sum up to n and the sum total
>>> s = [4,1,3,5]
>>> pack_helper(s, 7)
({3, 4}, 7)
>>> pack(s, 6)
({1, 5}, 6)
>>> pack(s, 11)
({1, 4, 5}, 10)
'''
package = set()
if L == []:
result = (package, sum)
else:
first = L[0]
(package, sum) = pack_helper(L[1:], n, sum)
if sum < n and (first + sum) <= n:
package.add(first)
sum = sum + first
return (package, sum)
Any hints or help? Thx
Here's a simple recursive function that does the job:
def pack(L, n):
'''Return the subset of L with the largest sum up to n
>>> s = [4,1,3,5]
>>> pack(s, 7)
{3, 4}
>>> pack(s, 6)
{1, 5}
>>> pack(s, 11)
{1, 4, 5}
'''
if all(j > n for j in L):
return set()
return max(({j} | pack(L[i+1:], n-j) for i, j in enumerate(L) if j <= n), key=sum)
If you're using Python 3, you can pass the default parameter to max instead:
def pack(L, n):
return max(({j} | pack(L[i+1:], n-j) for i, j in enumerate(L) if j <= n), key=sum, default=set())
The test data here is small enough that brute force is pretty fast. Recursion is not necessary:
from itertools import chain, combinations
# taken from the itertools documentation
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
def pack(L, n):
best_set, best_sum = (), 0
for candidate in powerset(L):
total = sum(candidate)
if best_sum < total <= n:
best_set, best_sum = candidate, total
return best_set
However, assuming positive weights, the dynamic programming solution is pretty short.
def pack(L, n):
assert all(w > 0 for w in L), 'weights must all be positive'
a = [((), 0)] * (n + 1)
for w in L:
a = [ (a[x - w][0] + (w,), a[x - w][1] + w)
if w <= x and a[x][1] < a[x - w][1] + w
else a[x] for x in range(n + 1) ]
return a[n][0]
How does this work?
a[x] stores the best set of weights processed so far that sum up to x or less (and the sum, just to save time). Before any weights have been processed, these are all empty ().
To process a new weight w at target x, one of the following two sets must be the best.
the best set of weights without this new weight that sum up to x (the old a[x]), or
the best set of weights without this new weight that sum up to x - w, plus this new weight w
Once all the weights are processed, the solution is right there at the end.
By the way, this is the well-known 0/1 knapsack problem. (The Wikipedia article currently has a solution that uses O(len(L) * n) time and O(len(L) * n) space, but it's doable in O(n) space, as I demonstrated here.)

bottom up fibonacci in python using O(1) space

I want to write a bottom up fibonacci using O(1) space. My problem is python's recursion stack is limiting me from testing large numbers. Could someone provide an alternate or optimization to what I have? This is my code:
def fib_in_place(n):
def fibo(f2, f1, i):
if i < 1:
return f2
else:
return fibo(f1, f2+f1, i -1)
return fibo(0, 1, n)
Using recursion this way means you're using O(N) space, not O(1) - the O(N) is in the stack.
Why use recursion at all?
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
You can memoize the Fibonacci function for efficiency, but if you require a recursive function, it's still going to take at least O(n):
def mem_fib(n, _cache={}):
'''efficiently memoized recursive function, returns a Fibonacci number'''
if n in _cache:
return _cache[n]
elif n > 1:
return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
return n
This is from my answer on the main Fibonacci in Python question: How to write the Fibonacci Sequence in Python
If you're allowed to use iteration instead of recursion, you should do this:
def fib():
a, b = 0, 1
while True: # First iteration:
yield a # yield 0 to start with and then
a, b = b, a + b # a will now be 1, and b will also be 1, (0 + 1)
usage:
>>> list(zip(range(10), fib()))
[(0, 0), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 8), (7, 13), (8, 21), (9, 34)]
If you just want to get the nth number:
def get_fib(n):
fib_gen = fib()
for _ in range(n):
next(fib_gen)
return next(fib_gen)
and usage
>>> get_fib(10)
55
Why use iteration at all?
def fib(n):
phi_1 = (math.sqrt(5) + 1) / 2
phi_2 = (math.sqrt(5) - 1) / 2
f = (phi_1**n - phi_2**n) / math.sqrt(5)
return round(f)
The algebraic result is exact; the round operation is only to allow for digital representation inaccuracy.
Tail-recursive definitions are easily turned into iterative definitions. If necessary, flip the condition so that the tail-recursive call is in the 'if' branch.
def fibo(f2, f1, i):
if i > 0:
return fibo(f1, f2+f1, i -1)
else:
return f2
Then turn 'if' into 'while', replace return with unpacking assignment of the new arguments, and (optionally) drop 'else'.
def fibo(f2, f1, i):
while i > 0:
f2, f1, i = f1, f2+f1, i -1
return f2
With iteration, you do not need the nested definition.
def fib_efficient(n):
if n < 0:
raise ValueError('fib argument n cannot be negative')
new, old = 0, 1
while n:
new, old = old, old+new
n -= 1
return new
Local names 'new' and 'old' refer to Fibonacci's use of biological reproduction to motivate the sequence. However, the story works better with yeast cells instead of rabbits. Old, mature yeast cells reproduce by budding off new, immature cells. (The original source of the function in India appears to be Virahanka counting the number a ways to make a Sanskrit poetic line with n beats from an ordered sequence of 1- and 2-beat syllables.)

Distinguish efficiently between different possible combinations in a tuple

I have a two-element tuple t, each element is either a positive integer or None, and the combination may be in one of the four forms:
1: (x, y): e.g. (2, 3)
2: (x, x): e.g. (1, 1)
3: (x, None) (or equivalently, (None, x)) : e.g. (3, None) or (None, 5)
4: (None, None)
My application logic wants to treat 2) and 3) as one case, 1) as the second case, and 4) as the third case.
I want to find an operation on a given tuple to make it easier/more efficient to distinguish between the three cases. For example, t[0] or t[1] will help us distinguish between the case of 2) and 3) and that of 4), but it cannot distinguish 2) and 3) from 1).
In the end, I want to minimize the number of if checks needed.
This should do:
if t[0] or t[1]:
if t[0] == t[1] or not (t[0] and t[1]):
# 2-3rd case
else:
# 1st case
else:
# 4th case
It's possible do it without if statements, like this horror code do:
def check(t):
[
lambda x: print(x, "match case 4"),
None,
lambda x: print(x, "match case 2 or 3"),
lambda x: print(x, "match case 1")
][sum((bool(t[0]), bool(t[1]), t[0] != t[1]))](t)
the key of solution is sum((bool(t[0]), bool(t[1]), t[0] != t[1])), this works because,
(x, y) pass in all checks,
(x, x) pass in two checks (bool(t[0]) and bool(t[1])),
(None, x) pass in two too (bool(t[1]) and t[0] != t[1]) and
(None, None) pass in no one

Categories

Resources