Using Python for quasi randomization - python

Here's the problem: I try to randomize n times a choice between two elements (let's say [0,1] -> 0 or 1), and my final list will have n/2 [0] + n/2 [1]. I tend to have this kind of result: [0 1 0 0 0 1 0 1 1 1 1 1 1 0 0, until n]: the problem is that I don't want to have serially 4 or 5 times the same number so often. I know that I could use a quasi randomisation procedure, but I don't know how to do so (I'm using Python).

To guarantee that there will be the same number of zeros and ones you can generate a list containing n/2 zeros and n/2 ones and shuffle it with random.shuffle.
For small n, if you aren't happy that the result passes your acceptance criteria (e.g. not too many consecutive equal numbers), shuffle again. Be aware that doing this reduces the randomness of the result, not increases it.
For larger n it will take too long to find a result that passes your criteria using this method (because most results will fail). Instead you could generate elements one at a time with these rules:
If you already generated 4 ones in a row the next number must be zero and vice versa.
Otherwise, if you need to generate x more ones and y more zeros, the chance of the next number being one is x/(x+y).

You can use random.shuffle to randomize a list.
import random
n = 100
seq = [0]*(n/2) + [1]*(n-n/2)
random.shuffle(seq)
Now you can run through the list and whenever you see a run that's too long, swap an element to break up the sequence. I don't have any code for that part yet.

Having 6 1's in a row isn't particularly improbable -- are you sure you're not getting what you want?
There's a simple Python interface for a uniformly distributed random number, is that what you're looking for?

Here's my take on it. The first two functions are the actual implementation and the last function is for testing it.
The key is the first function which looks at the last N elements of the list where N+1 is the limit of how many times you want a number to appear in a row. It counts the number of ones that occur and then returns 1 with (1 - N/n) probability where n is the amount of ones already present. Note that this probability is 0 in the case of N consecutive ones and 1 in the case of N consecutive zeros.
Like a true random selection, there is no guarantee that the ratio of ones and zeros will be the 1 but averaged out over thousands of runs, it does produce as many ones as zeros.
For longer lists, this will be better than repeatedly calling shuffle and checking that it satisfies your requirements.
import random
def next_value(selected):
# Mathematically, this isn't necessary but it accounts for
# potential problems with floating point numbers.
if selected.count(0) == 0:
return 0
elif selected.count(1) == 0:
return 1
N = len(selected)
selector = float(selected.count(1)) / N
if random.uniform(0, 1) > selector:
return 1
else:
return 0
def get_sequence(N, max_run):
lim = min(N, max_run - 1)
seq = [random.choice((1, 0)) for _ in xrange(lim)]
for _ in xrange(N - lim):
seq.append(next_value(seq[-max_run+1:]))
return seq
def test(N, max_run, test_count):
ones = 0.0
zeros = 0.0
for _ in xrange(test_count):
seq = get_sequence(N, max_run)
# Keep track of how many ones and zeros we're generating
zeros += seq.count(0)
ones += seq.count(1)
# Make sure that the max_run isn't violated.
counts = [0, 0]
for i in seq:
counts[i] += 1
counts[not i] = 0
if max_run in counts:
print seq
return
# Print the ratio of zeros to ones. This should be around 1.
print zeros/ones
test(200, 5, 10000)

Probably not the smartest way, but it works for "no sequential runs", while not generating the same number of 0s and 1s. See below for version that fits all requirements.
from random import choice
CHOICES = (1, 0)
def quasirandom(n, longest=3):
serial = 0
latest = 0
result = []
rappend = result.append
for i in xrange(n):
val = choice(CHOICES)
if latest == val:
serial += 1
else:
serial = 0
if serial >= longest:
val = CHOICES[val]
rappend(val)
latest = val
return result
print quasirandom(10)
print quasirandom(100)
This one below corrects the filtering shuffle idea and works correctly AFAICT, with the caveat that the very last numbers might form a run. Pass debug=True to check that the requirements are met.
from random import random
from itertools import groupby # For testing the result
try: xrange
except: xrange = range
def generate_quasirandom(values, n, longest=3, debug=False):
# Sanity check
if len(values) < 2 or longest < 1:
raise ValueError
# Create a list with n * [val]
source = []
sourcelen = len(values) * n
for val in values:
source += [val] * n
# For breaking runs
serial = 0
latest = None
for i in xrange(sourcelen):
# Pick something from source[:i]
j = int(random() * (sourcelen - i)) + i
if source[j] == latest:
serial += 1
if serial >= longest:
serial = 0
guard = 0
# We got a serial run, break it
while source[j] == latest:
j = int(random() * (sourcelen - i)) + i
guard += 1
# We just hit an infinit loop: there is no way to avoid a serial run
if guard > 10:
print("Unable to avoid serial run, disabling asserts.")
debug = False
break
else:
serial = 0
latest = source[j]
# Move the picked value to source[i:]
source[i], source[j] = source[j], source[i]
# More sanity checks
check_quasirandom(source, values, n, longest, debug)
return source
def check_quasirandom(shuffled, values, n, longest, debug):
counts = []
# We skip the last entries because breaking runs in them get too hairy
for val, count in groupby(shuffled):
counts.append(len(list(count)))
highest = max(counts)
print('Longest run: %d\nMax run lenght:%d' % (highest, longest))
# Invariants
assert len(shuffled) == len(values) * n
for val in values:
assert shuffled.count(val) == n
if debug:
# Only checked if we were able to avoid a sequential run >= longest
assert highest <= longest
for x in xrange(10, 1000):
generate_quasirandom((0, 1, 2, 3), 1000, x//10, debug=True)

Related

Check if a number can be formed by sum of a number and its reverse

I want to check if a given number can be formed by another number say b and reverse(b). For example 12 == 6+6, 22 == 11 + 11 and 121 == 29+92. One thing I have figured out is if the number is multiple of 11 or it is an even number less than 20, then it can be formed. I tried to implement this below:
num = 121
if num%11==0:
print('Yes')
else:
if num%2==0 and num<20:
print('Yes')
else:
for j in range(11,(int(num)//2)+1):
if j+int(str(j)[::-1])==num:
print('Yes')
break
However, if the condition goes into the for loop, it gives TLE. Can any other conditions be given?
Update: If the reversed number has trailing zeroes, it should be removed and then added. For example: 101 == 100+1. I am looking for an optimized form of my code. Or I think I am missing some conditions which can take O(1) time, similar to the condition if num%11==0: print('Yes')
All the previous answers are not really a check. It's more a brute force try and error.
So let's do it a little bit smarter.
We start with a number, for example 246808642. we can reduce the problem to the outer 2 place values at the end and start of the number. Let us call this values A and B at the front and Y and Z on the back. the rest, in the middle, is Π. So our number looks now ABΠYZ with A = 2, B = 4, Π = 68086, Y = 4 and Z = 2. (one possible pair of numbers to sum up for this is 123404321). Is A equal to 1, this is only possible for a sum greater 10 (An assumption, but i guess it works, some proof would be nice!).
so if it is a one, we know that the second last number is one greater by the carry over. So we ignore A for the moment and compare B to Z, because they should be the same because both are the result of the addition of the same two numbers. if so, we take the remaining part Π and reduce Y by one (the carry over from the outer addition), and can start again at the top of this chart with Π(Y-1). Only a carry over can make B one bigger than Z, if it's so, we can replace B by one and start with 1Π(Y-1) at the top. B-1!=Z and B!=Z, we can stop, this isnt possible for such a number which is the sum of a number and its reversed.
If A != 1, we do everything similiar as before but now we use A instead of B. (I cut this here. The answer is long enough.)
The code:
import time
def timing(f):
def wrap(*args, **kwargs):
time1 = time.time()
ret = f(*args, **kwargs)
time2 = time.time()
print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))
return ret
return wrap
#timing
def check(num):
num = str(num)
if (int(num) < 20 and int(num)%2 == 0) or (len(num) ==2 and int(num)%11 == 0):
return print('yes')
if len(num) <= 2 and int(num)%2 != 0:
return print('no')
# get the important place values of the number x
A = num[0]
B = num[1]
remaining = num[2:-2]
Y = num[-2]
Z = num[-1]
# check if A = 1
if A == '1':
# A = 1
# check if B == Z
if B == Z:
# so the outest addition matches perfectly and no carry over from inner place values is involved
# reduce the last digit about one and check again.
check(remaining + (str(int(Y)-1) if Y != '0' else '9'))
elif int(B)-1 == int(Z):
# so the outest addition matches needs a carry over from inner place values to match, so we add to
# to the remaining part of the number a leading one
# we modify the last digit of the remaining place values, because the outest had a carry over
check('1' + remaining + (str(int(Y)-1) if Y != '0' else '9'))
else:
print("Not able to formed by a sum of a number and its reversed.")
else:
# A != 1
# check if A == Z
if A == Z:
# so the outest addition matches perfectly and no carry over from inner place values is involved
check(B + remaining + Y)
elif int(A) - 1 == int(Z):
# so the outest addition matches needs a carry over from inner place values to match, so we add to
# to the remaining part of the number a leading one
# we modify the last digit of the remaining place values, because the outest had a carry over
check('1' + B + remaining + Y)
else:
print("Not able to formed by a sum of a number and its reversed.")
#timing
def loop_check(x):
for i in range(x + 1):
if i == int(str(x - i)[::-1]) and not str(x - i).endswith("0"):
print('yes, by brute force')
break
loop_check(246808642)
check(246808642)
Result:
yes, by brute force
loop_check function took 29209.069 ms
Yes
check function took 0.000 ms
And another time we see the power of math. Hope this work for you!
You can brute force it like this:
def reverse_digits(n):
return int(str(n)[::-1])
def sum_of_reversed_numbers(num):
for i in range(num + 1):
if i == reverse_digits(num - i):
return i, num - i
return None
print("Yes" if sum_of_reversed_numbers(num) else "No")
Can you provide the constraints of the problem?
Here is something you can try:
i = 0
j = num
poss = 0
while(i<=j):
if(str(i)==str(j)[::-1]):
poss = 1
break
i+=1
j-=1
if(poss):
print("Yes")
else:
print("No")
You can do it without str slicing:
def reverse(n):
r = 0
while n != 0:
r = r*10 + int(n%10)
n = int(n/10)
return r
def f(n):
for i in range(n + 1):
if i + reverse(i) == n:
return True
return False
print('Yes' if f(101) else 'No')
#Yes
The basic idea of my solution is that you first generate a mapping of digits to the digits that could make them up, so 0 can be made by either 0+0 or 1+9, 2+8 etc. (but in that case there's a carried 1 you have to keep in mind on the next step). Then you start at the smallest digit, and use that code to check each possible way to form the first digit (this gives you candidates for the first and last digit of the number that sums with its reverse to give you the input number). Then you move on the second digit and try those. This code could be greatly improved by checking both the last and the first digit together, but it's complicated by the carried 1.
import math
candidates = {}
for a in range(10):
for b in range(10):
# a, b, carry
candidates.setdefault((a + b) % 10, []).append((a, b, (a + b) // 10))
def sum_of_reversed_numbers(num):
# We reverse the digits because Arabic numerals come from Arabic, which is
# written right-to-left, whereas English text and arrays are written left-to-right
digits = [int(d) for d in str(num)[::-1]]
# result, carry, digit_index
test_cases = [([None] * len(digits), 0, 0)]
if len(digits) > 1 and str(num).startswith("1"):
test_cases.append(([None] * (len(digits) - 1), 0, 0))
results = []
while test_cases:
result, carry, digit_index = test_cases.pop(0)
if None in result:
# % 10 because if the current digit is a 0 but we have a carry from
# the previous digit, it means that the result and its reverse need
# to actually sum to 9 here so that the +1 carry turns it into a 0
cur_digit = (digits[digit_index] - carry) % 10
for a, b, new_carry in candidates[cur_digit]:
new_result = result[::]
new_result[digit_index] = a
new_result[-(digit_index + 1)] = b
test_cases.append((new_result, new_carry, digit_index + 1))
else:
if result[-1] == 0 and num != 0: # forbid 050 + 050 == 100
continue
i = "".join(str(x) for x in result)
i, j = int(i), int(i[::-1])
if i + j == num:
results.append((min(i, j), max(i, j)))
return results if results else None
We can check the above code by pre-calculating the sums of all numbers from 0 to 10ⁿ and their reverse and storing them in a dict of lists called correct (a list because there's many ways to form the same number, eg. 11+11 == 02 + 20), which means we have the correct answers for 10ⁿ⁻¹ we can use to check the above function. Btw, if you're doing this a lot with small numbers, this pre-calculating approach is faster at the expense of memory.
If this code prints nothing it means it works (or your terminal is broken :) )
correct = {}
for num in range(1000000):
backwards = int(str(num)[::-1])
components = min(num, backwards), max(num, backwards)
summed = num + backwards
correct.setdefault(summed, []).append(components)
for i in range(100000):
try:
test = sum_of_reversed_numbers(i)
except Exception as e:
raise Exception(i) from e
if test is None:
if i in correct:
print(i, test, correct.get(i))
elif sorted(test) != sorted(correct[i]):
print(i, test, correct.get(i))
Stole the idea from #Doluk. I was asked this question in a test today. I couldn't solve it then. With Doluk's idea and thinking seriously on it below is a decision tree kind for one level of recursion. I might be wrong as I haven't ran this algorithm.
Let n be the number, we want to check if special
case 1: leading number is not 1
case 1a: no carry over from inner addition
abcdefgh
hgfedcba
x x => (a+h) < 10
if both ends are same in n, strip both sides by one digit and recurse
case 1b: carry over from inner addition
1
abcdefgh
hgfedcba
(x+1)......(x) => (a+h+1) < 10
if left end is 1 greater than right end in n, strip both sides by one digit, add digit 1 on the left and recurse
case 2: leading number is 1
case 2a: no carry over from inner addition
1 1
abcdefgh
hgfedcba
1x x => (a+h) >= 10
strip - if second and last digit are same, strip two digits from left and one from right, from the remaining number minus 1 and recurse.
case 2b: carry over from inner addition
case 2bi: a+h = 9
11
abcdefgh
hgfedcba
10......9
strip - two from left and one from right and recurse.
case 2bj: a+h >= 10
11 1
abcdefgh
hgfedcba
1(x+1)......x
strip - two from left and one from right and subtract 1 from number and recurse.
In my question, they gave me an array of numbers and they asked me to find numbers which of them are special (Which can be formed by the sum and reverse of that number).
My brute force solution was to iterate from 0 to 1000000 and insert them into the set and last check for each element in the set.
Time Complexity: O(n)
Space Complexity: O(n)
where n is the highest number allowed.

Combination of all possible numbers following digit rules in python

Looking for a "pythonish" and simple way to achieve the generation of all possible numbers following different ranges of digits.
Example: Generate strings (representing number) with digits '01234567' ranging from [0-4] occurrences and '8' ranging from [0-10] occurrences and '9' from [0-100]
Example of numbers generated: {'11337899999999', '33567899999999', '245678999999999999999',...}
(all generated numbers should be with sequential digits... so '88119' isn't valid)
So far I came up with a very simple and clean code but that doesn't do exactly what I need:
from itertools import combinations_with_replacement
length = 50
for x in combinations_with_replacement('0123456789', length):
print("".join(x), end=', ')
This will generate the numbers I need but as well a bunch of unnecessary ones, which will delay significantly the execution.
Thought on generating the digits one by one according to the rules and concatenating... but believe this will be a "dirty" solution and as well inefficient.
Anyone knows a nice clean way of doing this? (Itertools, or any other library is welcome)
I am not sure I fully undersand you problem, but i think i have a solution :
import random
def generate(string = ""):
for i in range(9):
i += 1
if i <= 7:
occurrence = 4
elif i == 8:
occurrence = 10
else:
occurrence = 100
string = string + str(i)* random.randint(0, occurrence)
return string
a = generate()
print(a)
if you want it to be lenght 50 use:
a = a[:50]
So, I ended up picking the itertools.combinations_with_replacement and alter it to accept an array containing maximum usage of digits...
Pretty simple, but it's working.
If anyone has any hints on how to look at it differently or adapt anything to speed up, I will be happy with it.
def combinations_with_replacement2(iterable, m_digits, r):
pool = tuple(iterable)
n = len(pool)
if not n and r: return
count = [0] * n
indices = [0] * r
d = 0
for i in range(r):
if count[d] == m_digits[d]: d += 1
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != n - 1: break
else: return
count = [0] * n
d = indices[i] + 1
for f in range(i, r):
indices[f] = d
count[d] += 1
if count[d] == m_digits[d]: d += 1
yield tuple(pool[i] for i in indices)

Google Foobar Question - Please Pass The Coded Message

Google Foobar Question:
Please Pass the Coded Messages
You need to pass a message to the bunny prisoners, but to avoid detection, the code you agreed to use is... obscure, to say the least. The bunnies are given food on standard-issue prison plates that are stamped with the numbers 0-9 for easier sorting, and you need to combine sets of plates to create the numbers in the code. The signal that a number is part of the code is that it is divisible by 3. You can do smaller numbers like 15 and 45 easily, but bigger numbers like 144 and 414 are a little trickier. Write a program to help yourself quickly create large numbers for use in the code, given a limited number of plates to work with.
You have L, a list containing some digits (0 to 9). Write a function answer(L) which finds the largest number that can be made from some or all of these digits and is divisible by 3. If it is not possible to make such a number, return 0 as the answer. L will contain anywhere from 1 to 9 digits. The same digit may appear multiple times in the list, but each element in the list may only be used once.
Languages
To provide a Python solution, edit solution.py
To provide a Java solution, edit solution.java
Test cases
Inputs:
(int list) l = [3, 1, 4, 1]
Output:
(int) 4311
Inputs:
(int list) l = [3, 1, 4, 1, 5, 9]
Output:
(int) 94311
Use verify [file] to test your solution and see how it does. When you are finished editing your code, use submit [file] to submit your answer. If your solution passes the test cases, it will be removed from your home folder.
So that's the question, my python code only passes 3 out of 5 tests cases. I spent a few hours but can't find out what cases I am missing. Here is my code:
maximum = [0, 0, 0, 0, 0,0,0,0,0]
def subset_sum(numbers, target, partial=[]):
global maximum
s = sum(partial)
if s%3 == 0:
if s != 0:
str1 = ''.join(str(e) for e in partial)
y = int(str1)
str1 = ''.join(str(e) for e in maximum)
z = int(str1)
if y>z:
maximum = partial
# print maximum
if s >= target:
return # if we reach the number why bother to continue
for i in range(len(numbers)):
n = numbers[i]
remaining = numbers[i+1:]
subset_sum(remaining, target, partial + [n])
def answer(l):
global maximum
#maximum = [0, 0, 0, 0, 0]
subset_sum(l,sum(l))
maximum = sorted(maximum, key=int, reverse=True)
str1 = ''.join(str(e) for e in maximum)
y = int(str1)
return y
print(answer([3,1,4,1,5,9]))
So what test cases am I not accounting for, and how could I improve it?
try this using combination it may help:
from itertools import combinations
def answer(nums):
nums.sort(reverse = True)
for i in reversed(range(1, len(nums) + 1)):
for tup in combinations(nums, i):
if sum(tup) % 3 == 0: return int(''.join(map(str, tup)))
return 0
Presently, you are forming a number by using adjacent digits only while the question does not say so.
A quick fix would be to set remaining list properly -
remaining = numbers[:i] + numbers[i+1:]
But you need to think of better algorithm.
Update
inputNumbers = [2, 1, 1, 1, 7, 8, 5, 7, 9, 3]
inputNumSorted = sorted(inputNumbers)
sumMax = sum(inputNumbers)
queue = [(sumMax, inputNumSorted)]
found = False
while (len(queue) > 0):
(sumCurrent, digitList) = queue.pop()
remainder = sumCurrent%3
if (remainder == 0):
found = True
break
else :
for index, aNum in enumerate(digitList):
if(aNum%3 == remainder):
sumCurrent -= remainder
digitList.remove(aNum)
found = True
break
else:
newList = digitList[:index]+digitList[index+1:]
if (len(newList) > 0):
queue.insert(0, (sumCurrent-aNum, newList))
if(found):
break
maxNum = 0
if (found):
for x,y in enumerate(digitList):
maxNum += (10**x)*y
print(maxNum)
I believe the solution looks something like this:
Arrange the input digits into a single number, in order from largest to smallest. (The specific digit order won't affect its divisibility by 3.)
If this number is divisible by 3, you're done.
Otherwise, try removing the smallest digit. If this results in a number that is divisible by 3, you're done. Otherwise start over with the original number and try removing the second-smallest digit. Repeat.
Otherwise, try removing digits two at a time, starting with the two smallest. If any of these result in a number that is divisible by 3, you're done.
Otherwise, try removing three digits...
Four digits...
Etc.
If nothing worked, return zero.
Here's the actual solution and this passes in all the test cases
import itertools
def solution(l):
l.sort(reverse = True)
for i in reversed(range(1, len(l) + 1)):
for j in itertools.combinations(l, i):
if sum(tup) % 3 == 0: return int(''.join(map(str, j)))
return 0
Here is a commented solution (that passed all test cases):
def solution(l):
# sort in decending order
l = sorted(l, reverse = True)
# if the number is already divisible by three
if sum(l) % 3 == 0:
# return the number
return int("".join(str(n) for n in l))
possibilities = [0]
# try every combination of removing a single digit
for i in range(len(l)):
# copy list of digits
_temp = l[:]
# remove a digit
del _temp[len(_temp) - i - 1]
# check if it is divisible by three
if sum(_temp) % 3 == 0:
# if so, this is our solution (the digits are removed in order)
return int("".join(str(n) for n in _temp))
# try every combination of removing a second digit
for j in range(1, len(_temp)):
# copy list of digits again
_temp2 = _temp[:]
# remove another digit
del _temp2[len(_temp2) - j - 1]
# check if this combination is divisible by three
if sum(_temp2) % 3 == 0:
# if so, append it to the list of possibilities
possibilities.append(int("".join(str(n) for n in _temp2)))
# return the largest solution
return max(possibilities)
I tried a lot but test case 3 fails .Sorry for bad variable names
import itertools
def solution(l):
a=[]
k=''
aa=0
b=[]
for i in range(len(l)+1):
for j in itertools.combinations(l,i):
a.append(j)
for i in a:
if sum(i)>=aa and sum(i)%3==0 and len(b)<len(i):
aa=sum(i)
b=i[::-1]
else:
pass
b=sorted(b)[::-1]
for i in b:
k+=str(i)
if list(k)==[]:
return 0
else:
return k

Python 3: List of over 100 indices cycles back around after index 47. why? how do I stop this?

so this is a function that gets the nth prime number. I know its been done before and that my method may not be very efficient (new coder btw minor dabbling in the past).
Anyway the code below works and returns the prime number of the supplied index.
ie:
ind = 4
final[1,2,3,5,7,11]
return final[ind-1]
returns: 5
But final[51-1] returns whats in final[3-1]. Seems like after index 47 it loops back around and starts over. Ive printed the whole of the list contained in final. and it prints every prime, even those past 47. Im not sure whats going on. Is there some limit to lists in python?
Here is the code:
def nthPrime(ind): #gets nth prime number. IE: 5th prime == 11. works based off very in-efficient version of Sieve of Eratosthenes. but in increments of 200
p = {}
T = 2
incST = 2
incEND = incST + 200
final=[1]
while len(final) < ind:
for i in range(incST,incEND):
p[i] = True
while T <= math.sqrt(incEND):
l = 0
while l <= incEND:
p[T**2 + (T*l)] = False
l+=1
if T**2+(T*l) > incEND:
break
for k,v in p.items():
if p[k] == True and k > T:
T = int(k)
break
for k in p:
if p[k] == True:
final.append(k)
incST = incEND + 1
incEND = incST + 200
'''
currently function works perfectly for
any index under 48.
at index 48 and above it seems to start
back at index 1.
IE: final[51]
^would actually return final[4]
'''
return final[ind-1]
You need to count how many primes you have in your list, but you accumulate in final within the loop, so you add all the numbers up to the limit several times in the loop. Starts at 2 again after 199.
Also, using dictionaries and relying on the order is dangerous. You should sort them when iterating.
My solution only counts the primes to know when to end the loop, and compose the list just in the end, omitting 1 and shifting the index by 1.
I also sort the dictionary when iterating over it to make sure:
import math
def nthPrime(ind): #gets nth prime number. IE: 5th prime == 11. works based off very in-efficient version of Sieve of Eratosthenes. but in increments of 200
p = {}
T = 2
incST = 2
incEND = incST + 200
lenfinal = 1
while lenfinal < ind:
for i in range(incST,incEND):
p[i] = True
while T <= math.sqrt(incEND):
l = 0
while l <= incEND:
p[T**2 + (T*l)] = False
l+=1
if T**2+(T*l) > incEND:
break
for k,v in sorted(p.items()):
if v and k > T:
T = int(k)
break
incST = incEND + 1
incEND = incST + 200
# compute length, no need to order or to actually create the list
lenfinal = sum(1 for k,v in p.items() if v)
# now compose the list
final = [k for k,v in sorted(p.items()) if v]
return final[ind-2]
A more efficient way to do this would be a recursive function:
I'll put some explanation in the code.
def nthPrime(ind):
first_prime=1 #first prime number
number = 1 # all numbers that we will check, this will be incremented
prime_numbers = [first_prime] # The list of prime numbers we will find
def findPrimeInPosition(ind, number):
if ind > len(prime_numbers): # This recursive function will exit if find a sufficient number of primes
number+=1 # incrementing to check the next number
is_prime = True # Assuming number is a prime
for p in prime_numbers[1:]: # Check if it is a prime
if not number % p:
is_prime = False
if is_prime:
prime_numbers.append(number) # Add to the list of primes
findPrimeInPosition(ind, number)
return prime_numbers[-1] # Get the last element found
return findPrimeInPosition(ind, number)
Example of usage:
print nthPrime(47)
>> 199
print nthPrime(48)
>> 211
This isn't a Python issue, the problem is in your calculation on how you calculate the results. When you do final[51] it actually returns the value that holds that position, do this:
# Modify your line
# return final[ind-1]
# return final
# Call your method
o_final = nthPrime(100)
for k in range(len(o_final)):
print(k, y[k])
Then you realize that at pos 93 you reach the next one and keep incrementing.

Calculate the number of combinations of unique positive integers with minimum and maximum differences between each other?

How do I write a Python program to calculate the number of combinations of unique sorted positive integers over a range of integers that can be selected where the minimum difference between each of the numbers in the set is one number and the maximum difference is another number?
For instance, if I want to calculate the number of ways I can select 6 numbers from the positive integers from 1-50 such that the minimum difference between each number is 4 and the maximum difference between each number is 7, I would want to count the combination {1,6,12,18,24,28} since the minimum difference is 4 and the maximum difference is 6, but I would not want to count combinations like {7,19,21,29,41,49} since the minimum difference is 2 and the maximum difference is 12.
I have the following code so far, but the problem is that it has to loop through every combination, which takes an extremely long time in many cases.
import itertools
def min_max_differences(integer_list):
i = 1
diff_min = max(integer_list)
diff_max = 1
while i < len(integer_list):
diff = (integer_list[i]-integer_list[i-1])
if diff < diff_min:
diff_min = diff
if diff > diff_max:
diff_max = diff
i += 1
return (diff_min,diff_max)
def total_combinations(lower_bound,upper_bound,min_difference,max_difference,elements_selected):
numbers_range = list(range(lower_bound,upper_bound+1,1))
all_combos = itertools.combinations(numbers_range,elements_selected)
min_max_diff_combos = 0
for c in all_combos:
if min_max_differences(c)[0] >= min_difference and min_max_differences(c)[1] <= max_difference:
min_max_diff_combos += 1
return min_max_diff_combos
I do not have a background in combinatorics, but I am guessing there is a much more algorithmically efficient way to do this using some combinatorial methods.
You can use a recursive function with caching to get your answer.
This method will work even if you have a large array because some positions are repeated many times with the same parameters.
Here is a code for you (forgive me if I made any mistakes in python cause I don't normally use it).
If there is any flow in the logic, please let me know
# function to get the number of ways to select {target} numbers from the
# array {numbers} with minimum difference {min} and maximum difference {max}
# starting from position {p}, with the help of caching
dict = {}
def Combinations(numbers, target, min, max, p):
if target == 1: return 1
# get a unique key for this position
key = target * 1000000000000 + min * 100000000 + max * 10000 + p
if dict.has_key(key): return dict[key]
ans = 0
# current start value
pivot = numbers[p]
p += 1;
# increase the position until you reach the minimum
while p < len(numbers) and numbers[p] - pivot < min:
p += 1
# get all the values in the range of min <--> max
while p < len(numbers) and numbers[p] - pivot <= max:
ans += Combinations(numbers, target - 1, min, max, p)
p += 1
# store the ans for further inquiry
dict[key] = ans
return ans
# any range of numbers (must be SORTED as you asked)
numbers = []
for i in range(0,50): numbers.append(i+1)
# number of numbers to select
count = 6
# minimum difference
min = 4
# maximum difference
max = 7
ans = 0
for i in range(0,len(numbers)):
ans += Combinations(numbers, count, min, max, i)
print ans
Here is a very simple (and non-optimized) recursive approach:
Code
import numpy as np
from time import time
""" PARAMETERS """
SET = range(50) # Set of elements to choose from
N = 6 # N elements to choose
MIN_GAP = 4 # Gaps
MAX_GAP = 7 # ""
def count(N, CHOSEN=[]):
""" assumption: N > 0 at start """
if N == 0:
return 1
else:
return sum([count(N-1, CHOSEN + [val])
for val in SET if (val not in CHOSEN)
and ((not CHOSEN) or ((val - CHOSEN[-1]) >= MIN_GAP))
and ((not CHOSEN) or ((val - CHOSEN[-1]) <= MAX_GAP))])
start_time = time()
count_ = count(N)
print('used time in secs: ', time() - start_time)
print('# solutions: ', count_)
Output
('used time in secs: ', 0.1174919605255127)
('# solutions: ', 23040)
Remarks
It outputs the same solution as Ayman's approach
Ayman's approach is much more powerful (in terms of asymptotical speed)

Categories

Resources