Reverse-engineering conditional output for a function definition - python

I have defined a function to generate a list of prime numbers less than or equal to a given input which you can specify, based on the sieve of eratosthenes.
def eratosthenes(max_num):
primes = list(range(2,max_num+1))
for i in primes:
j=2
while i*j<= primes[-1]:
if i*j in primes:
primes.remove(i*j)
j=j+1
return primes
Now, if I wanted to find out which prime would be the 50th in the list, such that
len(primes) = 50
How would I go about this? Is it possible to reverse-engineer a defined function in this way?

You will get the 50th value from the list just with eratosthenes(300)[49]:
def eratosthenes(max_num):
primes = list(range(2,max_num+1))
for i in primes:
j=2
while i*j<= primes[-1]:
if i*j in primes:
primes.remove(i*j)
j=j+1
return primes
print(eratosthenes(300))
print(eratosthenes(300)[49])
which will yield
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293]
229
The counting of indices starts from zero, therefore 50th value can be extracted with list index value 49.
Then if you would like to limit then length of the list to 50, you may reverse-engineer by calling the function many times until the condition is met:
i = 2
while True:
primes = eratosthenes(i)
if len(primes) >= 50:
break
i = i + 1
print(i)
print(len(primes))
print(primes[49])
which yields
229
50
229

Why don't you call the function in a loop with increasingly large max numbers until the length of the output is 50, then print out the last item in the output?

Related

I have several lists, each containing 206 elements. When I attempt to reference an index within those 206, I'm given an error

I have several corresponding lists each containing exactly 206 elements. The number of elements and values of those elements can be different depending on what's inside the file the program reads.
One of those lists is comprised entirely of integers, and is used as the basis to determine if elements should be removed from all lists. My code follows:
toRemove = []
for element in myList:
if element < int(cutoff): # cutoff is specified by the user
toRemove.append(myList.index(element))
toRemove = [int(element) for element in toRemove]
for element in toRemove:
del myList2[element]
del myList3[element]
...
When I run >>>len(myList), Python returns 206 for all lists, minus toRemove as toRemove is determined according to user input. However, when I use toRemove to remove those indexes from all lists Python greets me with the following:
Traceback (most recent call last):
File "Test.py", line 104, in <module>
del myList2[element]
IndexError: list assignment index out of range
The highest value in toRemove is 204 -- there are 205 indices in this list. toRemove does not contain any values lower than 0. Typecasting [element] into [int(element)] throws the same error.
The list of indexes to remove:
[0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 15, 16, 17, 18, 20, 21, 22, 25, 26, 28, 29, 30, 31, 33, 34, 37, 38, 39, 40, 41, 45, 46, 47, 48, 51, 52, 54, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 72, 73, 74, 75, 77, 80, 84, 86, 87, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 106, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 118, 119, 120, 121, 124, 126, 127, 128, 129, 130, 133, 134, 135, 136, 138, 139, 140, 141, 142, 144, 145, 147, 149, 151, 110, 153, 154, 155, 157, 158, 161, 162, 163, 164, 165, 166, 167, 168, 169, 171, 173, 174, 175, 177, 178, 179, 180, 181, 182, 183, 184, 186, 187, 188, 189, 190, 192, 194, 195, 196, 197, 198, 200, 202, 203, 204]
As you can see, none less than 0 or greater than 205.
The list of numbers to check:
[1032, 3882, 4182, 6880, 1170, 1968, 2085, 3548, 9111, 1122, 11987, 12718, 7606, 8391, 8890, 2574, 1447, 6257, 1457, 11430, 7202, 6891, 1495, 12372, 12907, 2243, 3984, 11236, 3462, 4790, 1103, 1044, 8805, 1404, 6259, 8655, 13695, 6505, 3758, 2378, 3303, 3360, 9102, 14768, 12154, 3079, 3307, 2416, 7043, 9910, 10050, 6497, 2407, 13477, 4118, 13762, 1351, 3645, 4092, 3785, 1565, 3721, 4982, 4166, 11234, 7306, 3608, 2437, 4022, 2688, 11411, 9126, 3526, 1398, 5657, 2336, 8775, 4073, 14731, 8885, 7264, 8399, 10268, 12131, 2129, 11420, 1334, 3675, 1055, 3544, 5301, 6608, 1482, 14221, 2254, 4288, 1178, 5032, 4973, 19573, 3855, 4360, 6408, 3040, 3220, 11735, 3591, 3807, 2590, 6169, 2300, 1332, 8996, 6680, 3537, 1048, 3505, 5960, 3480, 1486, 4782, 4607, 18269, 8258, 4514, 8069, 5698, 1753, 6314, 1634, 3688, 9249, 18783, 5514, 1409, 7197, 3789, 8172, 2718, 5535, 3508, 1769, 2503, 8178, 2414, 1175, 13069, 1916, 12297, 1732, 69609, 3047, 2300, 5752, 1106, 1522, 11687, 3020, 3929, 9407, 13449, 4644, 2399, 4317, 4917, 5476, 1194, 4016, 1496, 7788, 9365, 1223, 12289, 1624, 1410, 3321, 12930, 1806, 7154, 4961, 2798, 5571, 1931, 7912, 4944, 10963, 2427, 7514, 2425, 2649, 1303, 13568, 2923, 11225, 5822, 4268, 5962, 2422, 6978, 12393, 1331, 12749, 7460, 1683, 6403, 11972]
You can substitute cutoff with 8000, as that is the number I am using to test the program.
What am I doing wrong, and how do I correct this problem?
The issue you're encountering is that the list shrinks as you remove elements from it. Since you're removing them in order from front to back, the later indexes may end up being off the end of the list.
Consider this simpler scenario than your list with 200+ values:
lst = [0, 1, 2, 3]
indexes_to_remove = [2, 3]
for i in indexes_to_remove:
del lst[i]
You'll get the same exception as in your example. The reason is that after you remove index 2, the last value (3) is also at index 2, since the list got shorter.
There are a few ways to fix this. One option would be to remove the elements starting at the end of the list, and working forwards. That way the indexes you remove later on will always still be valid, as nothing before them will have been removed. Since you're guaranteed to be adding the indexes in order (more or less, see my note below), you can just iterate in reverse over them with reversed:
for element in reversed(toRemove):
del myList2[element]
del myList3[element]
Another option would be to compute a modified index as you go about removing some of the values from the lists. This is not too difficult:
for i, element in enumerate(toRemove):
del myList2[element-i]
del myList3[element-i]
Finally, it might be more efficient to rebuild your whole lists, rather than using del to remove some values from them. del some_list[some_index] takes O(N) time to complete, so even if you have to loop over the indexes to skip several times, it's probably going to be faster than doing a bunch of del operations:
toRemove = set(toRemove)
myList2 = [v for i, v in enumerate(myList2) if i not in toRemove]
myList3 = [v for i, v in enumerate(myList3) if i not in toRemove]
A final note: Your current code may have another error finding all the indexes to remove if myList can have multiple copies of the same values. That's because myList.index(element) will always find the first occurrence of the value element, even if you were just iterating over it when it appears later in the list. Instead you should probably use enumerate to get the indexes as you iterate:
for i, element in enumerate(myList):
if element < int(cutoff): # cutoff is specified by the user
toRemove.append(i)
It might also be a good idea to change some of your variable names. You're currently using element for all your loops, and it sometimes refers to an index (in another list) and other times refers to a value. A more descriptive name would help distinguish between those cases!
As comments said, when you del something, the size of the list decreases and further elements shift (there's no gap).
What you can do is:
1. Use what you already used, but going from the highest index first.
2. Use your loop to mark elements for deletion (e.g. by assigning a None or other value not used in the list) rather than physically delete it - and after the loop filter them out (e.g. using myList2 = [elem for elem in myList2 if elem]).
3. Use list comprehension and avoid using loop altogether.
myList2 = [value for (index, value) in enumerate(myList2) if index not in toRemove]
I would zip all lists, having the first (int-valued) list as the first list, then filter the list of tuples based on the first element of each tuple and unzip the filtered list of tuples back into several lists.
In [2]: list1 = [1, 2, 3, 4, 5]
In [3]: list2 = list('abcde')
In [4]: list3 = list('12345')
In [5]: list(map(list, zip(*[x for x in zip(list1, list2, list3) if x[0] >= 3])))
Out[5]: [[3, 4, 5], ['c', 'd', 'e'], ['3', '4', '5']]
Is it what you'd like to have?
Reversing the list using myList.sort(reverse=True) solved my problem. Thanks!

How to get a list of combinations in z3py?

I have a list in python of integers like this:
myList = [97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57]
I want to get Z3 to output various sets or lists of numbers which are all members of myList... Essentially, I want to use Z3 to get additional lists of numbers which all exist in myList but are in various different orders. Put differently, I want to get various outputs from Z3 which contain numbers in the set myList above.
I am having trouble doing this with Z3py because I do not know how to have z3 return a list or a set as the model when I call s.model(), assuming s = Solver().
from z3 import *
myList = [97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57]
s = Solver ()
pick = []
for (i, v) in enumerate(myList):
b = Bool ('pick_%d' % i)
s.add(b == b)
pick += [(b, v)]
while (s.check() == sat):
m = s.model()
chosen = []
block = []
for (p, v) in pick:
if m.eval(p, model_completion=True):
chosen += [v]
block.append(Not(p))
else:
block.append(p)
print chosen
s.add(Or(block))
Note that this will print 2^n solutions where n is the number of elements in your list; so it'll take a while to finish if n is large!

How to check index if the N'th index should be greater than previous

I want to run a loop from 0 to 1000 i want to print numbers which is lower than previous digit "ex:123 3 is greater than 2 and 2 is greater than 1 so print 123" i tried from 1 to 100 and how to check for 1000 or greater numbers
i tried to convert int input to list and checking with 2 digits
no=int(input())
lis=[]
num_lis=[]
le=0
for i in range(10,no):
lis=str(i)
num_lis=[int (x)for x in lis]
le=len(num_lis)-1
if num_lis[le]>num_lis[le-1]:
print(i)
From 1 to 100 no problem i want to check three digits to like 1<2<3 if correct print i
my code only check last two digit how do i check for three and four digits
Printing all numbers that are lower then the one after:
You can simply remember one digit and print it if the next one is bigger:
number = None
while number is None:
number = int(input("Input a number: "))
number = str(number)
last_digit = int(number[0])
for s in number[1:]:
this_digit = int(s)
if this_digit > last_digit:
print(last_digit, end="")
last_digit = this_digit
print(last_digit)
Output for 12354:
1235
This prints all numbers that are lower then the next one.
Checking if numbers are "in ascending order":
To zimply check you can use zip(). Characters '0123456789' compare in this order: '0'<'1'<'2'<'3'<'4'<'5'<'6'<'7'<'8'<'9' - no need to convert it to an integer, simply compare the characters "as is":
def IsIncreasing(number):
n = str(number)
return all(a<b for a,b in zip(n,n[1:]))
How does this work?
It makes tuples from the number and number shifted by 1:
"123456789"
"23456789"
==> ('1','2'),('2','3'),...,('7','8'),('8','9') as generator of tuples
and ensures all first elements are smaller then the second element using all()
Example:
for k in [1234,1,123456798]:
print(k,IsIncreasing(k))
Output (reformatted):
1234 True
1 True
123456798 False
There is no need to compare via sorting which takes more computation.
Test all numbers from 1 to 1000:
You can create a list of all "increasing" numbers from 1 to 1000 using the IsIncreasing() function:
get_all_up_to_1000 = [k for k in range(1,1001) if IsIncreasing(k)]
print( *(f"{k:>3}," for k in get_all_up_to_1000))
Output:
1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15,
16, 17, 18, 19, 23, 24, 25, 26, 27, 28, 29, 34, 35,
36, 37, 38, 39, 45, 46, 47, 48, 49, 56, 57, 58, 59,
67, 68, 69, 78, 79, 89, 123, 124, 125, 126, 127, 128, 129,
134, 135, 136, 137, 138, 139, 145, 146, 147, 148, 149, 156, 157,
158, 159, 167, 168, 169, 178, 179, 189, 234, 235, 236, 237, 238,
239, 245, 246, 247, 248, 249, 256, 257, 258, 259, 267, 268, 269,
278, 279, 289, 345, 346, 347, 348, 349, 356, 357, 358, 359, 367,
368, 369, 378, 379, 389, 456, 457, 458, 459, 467, 468, 469, 478,
479, 489, 567, 568, 569, 578, 579, 589, 678, 679, 689, 789,
You could create a function that verifies if the digits of the number are sorted:
def int_sorted(i):
s = str(i)
return s == ''.join(sorted(s, key=int))
print(int_sorted(123))
print(int_sorted(1234))
print(int_sorted(4234))
Output
True
True
False
Note that sorted(s, key=int) sorts s (the string of digits) according to int value of each of the digits, by using the key parameter of sorted. This function works independent of the number of digits.
In case it must be greater than strict you could do:
def int_sorted(i):
s = str(i)
sorted_s = sorted(s, key=int)
return s == ''.join(sorted_s) and all(int(c) < int(n) for c, n in zip(sorted_s, sorted_s[1:]))
print(int_sorted(123))
print(int_sorted(1234))
print(int_sorted(4234))
print(int_sorted(99))
Output
True
True
False
False

How do I generate a list of random integers without consecutive numbers?

After I sort a randomly generated list, I have consecutive numbers, e.g.
[7, 9, 13, 47, 64, 76, 83, 94, 95, 114, 115, 116, 120, 121, 123, 124, 127, 136, 152, 154, 167, 184, 189, 205, 212, 222, 226, 229, 231, 238]
Here consecutive numbers are (94, 95), (120, 121) and (123, 124).
How do I remove them?
My code is:
while len(set(l)) != 30:
a = random.randint(1, 240)
l.append(a)
l = list(set(l))
l = sorted(l)
f.write(str(l))
I do not want to use randrange or choice from random module.
Create a random number a and check if a and a±1 are not in the set:
import random
l = set()
while len(l) != 30:
a = random.randint(1, 240)
if not {a-1,a,a+1} & l: # set intersection: empty == False == no common numbers
l.add(a)
l = sorted(l) # sorted creates a sorted list from any iterable
print(l)
Output:
[5, 12, 40, 47, 55, 59, 62, 73, 76, 82, 84, 89, 93, 95, 109,
125, 127, 141, 165, 168, 184, 187, 196, 202, 204, 210, 215,
218, 229, 231]
Directly using a set makes the check if the number (±1) is already part of your random numbers very fast.
Doku:
set.intersection (or &)
And as function:
import random
def get_random_numbers_no_neighboring_elems(min_num, max_num, amount):
"""Generates amount random numbers in [min_num,..,max_num] that do not
include neighboring numbers."""
# this is far from exact - it is best to have about 5+ times the amount
# of numbers to choose from - if the margin is too small you might take
# very long to get all your "fitting numbers" as only about 1/4 of the range
# is a viable candidate (worst case):
# [1 2 3 4 5 6 7 8 9 10]: draw 2 then 5 then 8 and no more are possible
if (max_num-min_num) // 5 < amount:
raise ValueError(f"Range too small - increase given range.")
l = set()
while len(l) != amount:
a = random.randint(min_num, max_num)
if not {a-1,a,a+1} & l: # set intersection: empty == False == no commons
l.add(a)
return sorted(l)
print(get_random_numbers_no_neighboring_elems(1,240,80))
I add a solution where edges can be selected with same probability than others, but they need to be mutually exclusive :
def rand2(k,n,edge=False):
forbid=set()
l=set()
while len(l)<k:
x=random.randint(1,n)
if x not in forbid :
l.add(x)
forbid.update({x,x-1,x+1})
if edge and x in {1,n} : forbid.add(n+1-x)
return l
from collections import Counter
print(Counter([tuple(rand2(2,5)) for i in range(10000)]))
#Counter({(2, 4):1943, (1, 4):1720, (3, 5):1711, (2, 5):1652, (1, 3):1637, (1, 5):1337})
print(Counter([tuple(rand2(2,5,edge=True)) for i in range(100000)]))
#Counter({(2, 5): 2060, (3, 5): 2026, (1, 4): 1981, (1, 3): 1975, (2, 4): 1958})

Repeated Random Numbers

I have created a bingo game where random numbers are generated and called for a list.
bingo_num = random.randint(1,100)
How would I stop a random number being generated more than once
I would suggest, random.shuffle
from random import shuffle
my_list = range(100)
shuffle(my_list)
print my_list
But if you need only specific amount of unique numbers, you can use random.sample, like this
from random import sample
my_list = range(100)
print sample(my_list, 10)
You can take a sample of a range from the random library
>>> import random
>>> nums = random.sample(range(0,200),100)
>>> nums
[143, 149, 52, 183, 161, 179, 180, 155, 163, 157, 139, 15, 154, 181, 56, 29, 31,
14, 77, 82, 165, 32, 35, 92, 109, 172, 69, 99, 54, 3, 88, 76, 11, 126, 78, 162,
198, 145, 124, 75, 114, 174, 136, 100, 190, 193, 148, 153, 167, 113, 38, 17, 16
8, 0, 196, 73, 47, 164, 184, 6, 140, 30, 58, 74, 4, 79, 147, 178, 191, 21, 112,
13, 27, 57, 199, 116, 28, 104, 111, 71, 23, 85, 170, 25, 141, 156, 91, 7, 182, 1
34, 94, 169, 175, 166, 137, 160, 129, 36, 67, 135]
Just do it the same way they do it in a real Bingo game. They do not roll dices, but put all the numbers in a big bag, shake it, and pull out numbers one at a time, until all the numbers are used up.
numbers = list(range(1, 101)) # all the numbers in the bag, from 1 to 100
random.shuffle(numbers) # shake the bag
bingo_num = numbers.pop() # pull out next number (inside your loop)
You could first create a list containing all possible numbers. Then pick a random number from that list, add that to a result list and finally remove it from the list of possible numbers.
For example if you want 5 different numbers from 0-9:
possible_numbers = range(10)
numbers = []
for i in range(5):
index = random.randint(0, len(possible_numbers) - 1)
numbers.append(possible_numbers[index])
del possible_numbers[index]
Something like this should work:
numbers = []
while len(numbers) < 100:
bingo_num = random.randint(1,100)
if not bingo_num in numbers:
numbers.append(bing_num)
You could just create a new number if already in use
advantage: easy code
disadvantage: will run long if nearly all numbers are use (there are better solutions)
Sample code:
bingo_num_list = []
# init num
bingo_num = random.randint(1,100)
# create new numbers till "find" a not used one
while bingo_num in bingo_num_list:
bingo_num = random.randint(1,100)
bingo_num_list.append(bingo_num)
Build list of all the numbers then select a random 1, remove it from the list then select the next one:
#!/usr/bin/python
import random
myNumz=[]
xIdx=1
while xIdx<101:
myNumz.insert(xIdx,xIdx)
xIdx+=1
xIdx=100
while xIdx>0:
xIdx-=1
baseNum=random.randint(0,xIdx)
print myNumz[baseNum]
myNumz.remove(myNumz[baseNum])

Categories