Update: I realize that I put the question very badly. Here's a second run.
Consider the following function:
myList = []
optimumList = []
def findOptimumListItems():
n = 5
for i in range (n + 1):
for j in range (n + 1 - i):
myList.append((i, j, n-i-j))
for i in myList:
win = 0.0
draw = 0.0
for j in myList:
score = 0
if (i[0] > j[0]):
score += 1
if (i[0] == j[0]):
score += 0.5
if (i[1] > j[1]):
score += 1
if (i[1] == j[1]):
score += 0.5
if (i[2] > j[2]):
score += 1
if (i[2] == j[2]):
score += 0.5
if (score == 2):
win += 1
if (score == 1.5):
draw += 1
if (win/(len(myList)-win-draw) > 1.0):
optimumList.append(i)
return optimumList
First I make a list. For n = 5 the generated list is:
[(0, 0, 5), (0, 1, 4), (0, 2, 3), (0, 3, 2), (0, 4, 1),
(0, 5, 0), (1, 0, 4), (1, 1, 3), (1, 2, 2), (1, 3, 1),
(1, 4, 0), (2, 0, 3), (2, 1, 2), (2, 2, 1), (2, 3, 0),
(3, 0, 2), (3, 1, 1), (3, 2, 0), (4, 0, 1), (4, 1, 0),
(5, 0, 0)]
Then, the function takes each element of the list and compares it with the list itself. This is how you do it: Say I'm comparing [0, 0, 5] against [3, 1, 1]. 0 loses to 3 (so no points), 0 loses to 1, so no points, 5 wins against 1 (1 point for that). A draw gets 0.5 points, a win gets 1 point. For any item, if wins are more than loses then that item is considered optimum and is added to the optimum list.
For n = 5, the optimum list is:
[(0, 2, 3), (0, 3, 2), (1, 1, 3), (1, 2, 2), (1, 3, 1), (2, 0, 3),
(2, 1, 2), (2, 2, 1), (2, 3, 0), (3, 0, 2), (3, 1, 1), (3, 2, 0)]
My question is: How can I write the above function in a concise way? I'm especially interested in functional algorithms. Python, Ruby, Java, Haskell answers will be appreciated. (Having said that, if you have a neat solution in any language; that's okay.)
Sorry for repeating the same question. I agree that the original question was messy and hard to understand. I hope it's clear now.
Update (upon rampion's comment): Is there an efficient algorithm for this (or this type) problem?
Second Update: Great -- now I understand exactly what you want. This does the same thing as the code in your most recent edit:
def optimize(myList):
score_tup = lambda tup_a, tup_b: sum(1.0 if a > b else 0.5 if a == b else 0 for a, b in zip(tup_a, tup_b))
scores = ((tup_a, [score_tup(tup_a, tup_b) for tup_b in myList]) for tup_a in myList)
scores = ((tup, score.count(2), score.count(1.5)) for tup, score in scores)
return [tup for tup, win, draw in scores if (win * 1.0 / (len(myList) - win - draw)) > 1.0]
a = 5
myList = [(i, j, a-i-j) for i in range(a + 1) for j in range(a + 1 - i)]
print myList
print optimize(myList)
If you want to see previous versions of this answer, check the edits; this was getting too long.
in Haskell:
optimize :: Int -> [(Int,Int,Int)]
optimize n = filter optimal [ (a,b,c) | a <- [0..n], b <- [0..(n-a)], let c = n - a - b ]
where optimal x = (>0) . sum $ map (comp x) xs
comp (a,b,c) (a',b',c') = signum $ vs a a' + vs b b' + vs c c'
vs x x' = case compare x x' of
GT -> 1
EQ -> 0
LT -> -1
Though this is fairly concise, it's not very efficient (we compare (0,3,2) with (0,2,3) and vice versa, when we only need to do that once).
This isn't done yet, but it's a good start, I think.
It's written in Ruby.
>> l = [1,2,3]
>> l.map {|n| l.map{|i| i > n ? 1 : 0.5 }}.flatten.inject(0){|start, n| start + n}
=> 6.0
What is this for? Comparing each item in the list with every other item in the list will take an extremely large time ( O(n^2), I believe), especially as the list grows in size. If you give us some context, we may be able to tell you a better way to do this.
Anyway, here's what I came up with for comparing all of your items:
>>> for i in range(len(myList)):
... for x in range(len(myList)):
... if x != i:
... if myList[i][0] > myList[x][0]:
... score += 1
... if myList[i][0] < myList[x][0]:
... score += .5
...
Untested, as it never finished running, so there may be a mistake.
If I'm doing this correctly the comparison function is non-transformative, and what is returned is the same list you had before the comparison. That being said my functional program for generating the list is
def triple_tuple_generator(a):
"""Given an integer 'a', returns a generator of triple tuples of length 'a(a-1), where the tuple values are over the range 'a-1=i' (i,i-1,a-2*i+1)."""
for i in range(a):
for j in range(a-1):
yield (i,j,a-1-i-j)
This is a generator so consume as you wish. If I was good enough at working with summations I would prove my hunch, but I'm a physicist not a mathematician. ;) Let me know if I got this right.
Related
First of all, I apologise for the title, I did not know how to put my problem in words. Well, here it is:
For an integer a greater than 1, let F be a sorted list of prime factors of a. I need to find all tuples c (filled with whole numbers), such that length of each tuple is equal to the size of F and (F[0] ** c[0]) * (F[1] ** c[1]) * (...) < a. I should add that I write in Python.
Example:
a = 60
F = [2,3,5]
# expected result:
C = {(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 2, 0),
(0, 2, 1), (0, 3, 0), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1),
(1, 2, 0), (1, 3, 0), (2, 0, 0), (2, 0, 1), (2, 1, 0), (2, 2, 0), (3, 0, 0),
(3, 0, 1), (3, 1, 0), (4, 0, 0), (4, 1, 0), (5, 0, 0)}
I generated this result using itertools.product(), specifically:
m = math.floor(round(math.log(a, min(F)), 12))
for i in itertools.product(range(m + 1), repeat=len(F)):
if math.prod([F[j] ** i[j] for j in range(len(F))]) < a: print(i)
I think it works but it's inefficient. For example number 5 appears only in one tuple, but was checked many more times! Is there any way to make it faster? I would use multiple while loops (with break statements) but since I don't know what is the length of F, I don't think that is possible.
You base all your range limits on just min(F). Let's customize each to the log(a, factor) to reduce the cases:
from math import ceil, log, prod
from itertools import product
a = 60
F = [2, 3, 5]
ranges = [range(0, ceil(log(a, factor))) for factor in F]
C = []
for powers in product(*ranges):
if prod(F[i] ** power for i, power in enumerate(powers)) < a:
C.append(powers)
print(C)
By my measure, your code generates 216 test cases to come up with 25 results, but the above code only generates 1/3 of those test cases.
You could iterate over all the "valid" tuples with a generator, like so:
def exponent_tuples(prime_factors, limit):
def next_tuple(t):
n = math.prod(f ** tt for f, tt in zip(prime_factors, t))
for idx, (f, tt) in enumerate(zip(prime_factors, t)):
n *= f
if n < limit:
return (0,) * idx + (tt + 1,) + t[idx + 1 :]
n //= f**(tt+1)
return None
t = (0,) * len(prime_factors)
while t is not None:
yield t
t = next_tuple(t)
for t in exponent_tuples([2, 3, 5], 60):
print(t)
The idea here is to basically increment the tuple entries like digits of a number and have the respective digit roll over to zero and carry the 1 whenever you reach the defined limit.
I'm pretty sure this does exactly what you want, except for maybe the order in which it yields the tuples (can be adjusted by modifying the next_tuple function)
EDIT: Simplified the code a bit
The almost cooked proposition would go like this (shell execution)
>>> max_exponents(42,[2,3,7])
[5, 3, 1]
>>> #pick 2
>>> max_exponents(42//2**2,[3,7])
[2, 1]
>>> #pick 1
>>> max_exponents(42//(2**2*3**1),[7])
[0]
I'm almost done. This will adapt to any number of factors !
Somehow your proposition reduces to this (more readable form ?)
import math as m
import pprint
a = 60
prime_factors = [2,3,5]
exponents =list(map(lambda x:m.floor(m.log(a,x)),prime_factors))
rez = []
for i in range(exponents[0]+1):
for j in range(exponents[1]+1):
for k in range(exponents[2]+1):
if 2**i*3**j*5**k <= a:
rez.append((i,j,k))
pprint.pprint(rez)
and you would like to know wether there's a way to make if faster (with less tests). So we're no more on the implementation side, but more on the conception (algorithm) side ?
For example, once the first exponent c[0] has been chosen, the next ones should be selected amongst the one fitting in a//(2**c[a]) as the other answerer proposed i guess
I want to be able to find all the different ways a set of numbers(x) can be summed into a certain value, y but I'm having trouble even getting the base case right.
For example:
If I have x = set (1,2,3,4,5) and I want to see how many different ways y = 5 can be summed up using numbers from x:
my recursive function would return 7 because:
'''
5
4+1
3+2
3+1+1
2+2+1
2+1+1+1
1+1+1+1+1
'''
def recur(x,y):
if y == x:
v += 1
if y > x:
v += 0
else:
#call recursively
This does not use recursion, but itertools.combinations_with_replacement:
def all_combs(y, x=range(1, 5+1)):
all_combs = []
for i in range(1, y+1):
combs = combinations_with_replacement(x, i)
all_combs.extend([comb for comb in combs if sum(comb) == y])
return all_combs
combs = all_combs(5)
# [(5,), (1, 4), (2, 3), (1, 1, 3), (1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1)]
num_combs = len(combs) # 7
I have some permutations of a list:
>>> import itertools
>>> perms = list(itertools.permutations([0,1,2,3]))
>>> perms
[(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2), (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (1, 2, 0, 3), (1, 2, 3, 0), (1, 3, 0, 2), (1, 3, 2, 0), (2, 0, 1, 3), (2, 0, 3, 1), (2, 1, 0, 3), (2, 1, 3, 0), (2, 3, 0, 1), (2, 3, 1, 0), (3, 0, 1, 2), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 0, 1), (3, 2, 1, 0)]
>>> len(perms)
24
What function can I use (without access to the list perm) to get the index of an arbitrary permutation, e.g. (0, 2, 3, 1) -> 3?
(You can assume that permuted elements are always an ascending list of integers, starting at zero.)
Hint: The factorial number system may be involved. https://en.wikipedia.org/wiki/Factorial_number_system
Off the top of my head I came up with the following, didn't test it thoroughly.
from math import factorial
elements = list(range(4))
permutation = (3, 2, 1, 0)
index = 0
nf = factorial(len(elements))
for n in permutation:
nf //= len(elements)
index += elements.index(n) * nf
elements.remove(n)
print(index)
EDIT: replaced nf /= len(elements) with nf //= len(elements)
I suppose this is a challenge, so here is my (recursive) answer:
import math
import itertools
def get_index(l):
# In a real function, there should be more tests to validate that the input is valid, e.g. len(l)>0
# Terminal case
if len(l)==1:
return 0
# Number of possible permutations starting with l[0]
span = math.factorial(len(l)-1)
# Slightly modifying l[1:] to use the function recursively
new_l = [ val if val < l[0] else val-1 for val in l[1:] ]
# Actual solution
return get_index(new_l) + span*l[0]
get_index((0,1,2,3))
# 0
get_index((0,2,3,1))
# 3
get_index((3,2,1,0))
# 23
get_index((4,2,0,1,5,3))
# 529
list(itertools.permutations((0,1,2,3,4,5))).index((4,2,0,1,5,3))
# 529
You need to write your own function. Something like this would work
import math
def perm_loc(P):
N = len(P)
assert set(P) == set(range(N))
def rec(perm):
nums = set(perm)
if not perm:
return 0
else:
sub_res = rec(perm[1:]) # Result for tail of permutation
sub_size = math.factorial(len(nums) - 1) # How many tail permutations exist
sub_index = sorted(nums).index(perm[0]) # Location of first element in permutaiotn
# in the sorted list of number
return sub_index * sub_size + sub_res
return rec(P)
The function that does all the work is rec, with perm_loc just serving as a wrapper around it. Note that this algorithm is based on the nature of the permutation algorithm that itertools.permutation happens to use.
The following code tests the above function. First on your sample, and then on all permutations of range(7):
print perm_loc([0,2,3,1]) # Print the result from the example
import itertools
def test(N):
correct = 0
perms = list(itertools.permutations(range(N)))
for (i, p) in enumerate(perms):
pl = perm_loc(p)
if i == pl:
correct += 1
else:
print ":: Incorrect", p, perms.index(p), perm_loc(N, p)
print ":: Found %d correct results" % correct
test(7) # Test on all permutations of range(7)
from math import factorial
def perm_to_permidx(perm):
# Extract info
n = len(perm)
elements = range(n)
# "Gone"s will be the elements of the given perm
gones = []
# According to each number in perm, we add the repsective offsets
offset = 0
for i, num in enumerate(perm[:-1], start=1):
idx = num - sum(num > gone for gone in gones)
offset += idx * factorial(n - i)
gones.append(num)
return offset
the_perm = (0, 2, 3, 1)
print(perm_to_permidx(the_perm))
# 3
Explanation: All permutations of a given range can be considered as a groups of permutations. So, for example, for the permutations of 0, 1, 2, 3 we first "fix" 0 and permute rest, then fix 1 and permute rest, and so on. Once we fix a number, the rest is again permutations; so we again fix a number at a time from the remaining numbers and permute the rest. This goes on till we are left with one number only. Every level of fixing has a corresponding (n-i)! permutations.
So this code finds the "offsets" for each level of permutation. The offset corresonds to where the given permutation starts when we fix numbers of perm in order. For the given example of (0, 2, 3, 1), we first look at the first number in the given perm which is 0, and figure the offset as 0. Then this goes to gones list (we will see its usage). Then, at the next level of permutation we see 2 as the fixing number. To calculate the offset for this, we need the "order" of this 2 among the remaining three numbers. This is where gones come into play; if an already-fixed and considered number (in this case 0) is less than the current fixer, we subtract 1 to find the new order. Then offset is calculated and accumulated. For the next number 3, the new order is 3 - (1 + 1) = 1 because both previous fixers 0 and 2 are at the "left" of 3.
This goes on till the last number of the given perm since there is no need to look at it; it will have been determined anyway.
I have a list d of length r such that d = (d_1, d_2,..., d_r).
I would like to generate all possible vectors of length r such that for any i (from 0 to r), v_i is between 0 and d_i.
For example,
if r =2 and d= (1,2), v_1 can be 0 or 1 and v_2 can be 0,1 or 2.
Hence there are 6 possible vectors:
[0,0] , [0,1], [0,2], [1,0] , [1,1], [1,2]
I have looked into Itertools and combinations and I have a feeling I will have to use recursion however I have not managed to solve it yet and was hoping for some help or advice into the right direction.
Edit:
I have written the following code for my problem and it works however I did it in a very inefficient way by disregarding the condition and generating all possible vectors then pruning the invalid ones. I took the largest d_i and generated all vectors of size r from (0,0,...0) all the way to (max_d_i,max_d_i,....max_d_i) and then eliminated those that were invalid.
Code:
import itertools
import copy
def main(d):
arr = []
correct_list =[]
curr = []
r= len(d)
greatest = max(d)
for i in range(0,greatest+1):
arr = arr + [i]
#all_poss_arr is a list that holds all possible vectors of length r from (0,0,...,0) to (max,max,...,max)
# for example if greatest was 3 and r= 4, all_poss_arr would have (0,0,0,0), then (0,0,0,1) and so on,
#all the way to (3,3,3,3)
all_poss_arr = list(itertools.product(arr,repeat = r))
#Now I am going to remove all the vectors that dont follow the v_i is between 0 and d_i
for i in range(0,len(all_poss_arr)):
curr = all_poss_arr[i]
cnt = 0
for j in range(0,len(curr)):
if curr[j] <= d[j]:
cnt = cnt +1
if cnt == r:
curr = list(curr)
currcopy = copy.copy(curr)
correct_list = correct_list + [currcopy]
cnt =0
return correct_list
If anyone knows a better way, let me know, it is much appreciated.
You basically want a Cartesian product. I'll demonstrate a basic, functional and iterative approach.
Given
import operator as op
import functools as ft
import itertools as it
def compose(f, g):
"""Return a function composed of two functions."""
def h(*args, **kwargs):
return f(g(*args, **kwargs))
return h
d = (1, 2)
Code
Option 1: Basic - Manual Unpacking
list(it.product(range(d[0] + 1), range(d[1] + 1)))
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Option 2: Functional - Automated Mapping
def vector_combs(v):
"""Return a Cartesian product of unpacked elements from `v`."""
plus_one = ft.partial(op.add, 1)
range_plus_one = compose(range, plus_one)
res = list(it.product(*map(range_plus_one, v)))
return res
vector_combs(d)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Option 3: Iterative - Range Replication (Recommended)
list(it.product(*[range(x + 1) for x in d]))
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Details
Option 1
The basic idea is illustrated in Option 1:
Make a Cartesian product using a series of modified ranges.
Note, each range is manually incremented and passed in as an index from d. We automate these limitations in with the last options.
Option 2
We apply a functional approach to handle the various arguments and functions:
Partial the 1 argument to the add() function. This returns a function that will increment any number.
Let's pass this function into range through composition. This allows us to have a modified range function that auto increments the integer passed in.
Finally we map the latter function to each element in tuple d. Now d works with any length r.
Example (d = (1, 2, 1), r = 3):
vector_combs((1, 2, 1))
# [(0, 0, 0),
# (0, 0, 1),
# (0, 1, 0),
# (0, 1, 1),
# (0, 2, 0),
# (0, 2, 1),
# (1, 0, 0),
# (1, 0, 1),
# (1, 1, 0),
# (1, 1, 1),
# (1, 2, 0),
# (1, 2, 1)]
Option 3
Perhaps most elegantly, just use a list comprehension to create r ranges. ;)
I'd like to go through all n-digit numbers such that second digit of the number is always lower or equal to the first, third is lower or equal to the second etc. I can get this by writing a horrible code such as:
for i in range(10):
for j in range(i+1):
for k in range(j+1):
etc., but with 10-digit numbers my code starts looking horrible, and also that's a lot of writing, and indentation get horrible if I want to commend few of those. Is there a nice, concise way of getting this?
Edit: just so that people know why I'm bothering with this, https://projecteuler.net/problem=74 has me check numbers from 1 to one milion. Unfortunately, It's not as straightforward as I thought -- numbers with leading zeros are treated differently than the ones with zeros inside, so some additional magic had to be performed. Anyway, thanks to all for insightful suggestions.
Could use itertools:
>>> for comb in itertools.combinations_with_replacement(range(9, -1, -1), 3):
print comb
(9, 9, 9)
(9, 9, 8)
(9, 9, 7)
(9, 9, 6)
...
(4, 0, 0)
(3, 3, 3)
(3, 3, 2)
(3, 3, 1)
(3, 3, 0)
(3, 2, 2)
(3, 2, 1)
(3, 2, 0)
(3, 1, 1)
(3, 1, 0)
(3, 0, 0)
(2, 2, 2)
(2, 2, 1)
(2, 2, 0)
(2, 1, 1)
(2, 1, 0)
(2, 0, 0)
(1, 1, 1)
(1, 1, 0)
(1, 0, 0)
(0, 0, 0)
Or recursively, appending more and more digits until enough, which can more directly produce int objects instead of digit tuples (not sure whether that's what you actually need):
def build(enough, prefix=0):
if prefix >= enough:
print(prefix)
return
for digit in range(prefix % 10 + 1) if prefix else range(1, 10):
build(enough, prefix * 10 + digit)
Demo (note it leaves out "000", not sure whether you'd want that anyway):
>>> n = 3
>>> build(10**(n-1))
100
110
111
200
210
211
220
221
222
300
310
311
320
321
322
330
331
332
333
400
410
411
420
this an approach using itertools:
from itertools import combinations_with_replacement
N = 3
for kji in combinations_with_replacement((str(i) for i in range(10)), N):
print(''.join(reversed(kji)))
note that the order is not the same as in your original approach.
i recently had a simliar question...
A simple recursive approach
def ordered_digits_generator(numDigits,min=1,max=9):
for first in range(min,max+1):
if numDigits == 1:
yield first
else:
addend = first*10**(numDigits-1)
for rest in ordered_digits(numDigits-1,min=0,max=first):
yield addend+rest
Then called via:
for number in ordered_digits_generator(10):
print number
works as expected.
The mathematician's approach
The itertools package already has logic which essentially already implements this recursion. Presumably better than the first approach, with significant testing. So we can use it as follows:
import itertools
def ordered_digits_combo(numDigits):
exponent = [10**i for i in range(0,numDigits)]
for subset in itertools.combinations(range(0,numDigits+9),numDigits):
if subset[numDigits-1]>numDigits-1:
v = 0
for i in range(0,numDigits):
v += exponent[i]*(subset[i]-i)
yield v
Given an ordered subset a[0]<a[1]<...<a[n-1] of {0,1,...,n+8}, we pick the number with the ith digit from the right equal to a[i]-i. We have to exclude the case a[n-1]==n-1 because that consists of a number with all zeros.
I implemented #iFlo's suggestion as commented originally. It's not hyper efficient but it certainly doesn't take ages.
def digit_test(n):
while n > 9:
if (n % 100 / 10) < (n % 10): return False
n /= 10
return True
# under a second to construct a list of all numbers below 1000000 meeting the criteria
candidates = [x for x in xrange(1,1000000) if digit_test(x)]
# should be 8001 elements, consistent with other algorithms
print len(candidates)
I would probably implement this recursively:
def generate(max, digits):
for d in range(max + 1):
if digits == 1:
yield d
else:
first = d * 10**(digits-1)
for n in generate(d, digits - 1):
yield first + n
The output:
In : list(generate(3, 3))
Out:
[0,
100,
110,
111,
200,
210,
211,
220,
221,
222,
300,
310,
311,
320,
321,
322,
330,
331,
332,
333]