Collatz Conjecture Sequence iterations and multiple divisor - python

For the case, the sequence does not end in 1. How can I create a code that has some way of stopping the execution if 1 is not reached after a certain number of iterations? I just don't know how to add this to my code below. And is there a way to make it work with a list of divisors such as [2, 3]? I'm using Python.
import numpy
#change the function to have 3 input args, the number, the multiplier and the
divisor
def collatz(n,multiplier,divisor):
list1 = [n]
if n == 1 :
return [1]
elif n % 2 and n % 3 == 0 :
#edit the function's input args for both the else and elif loops
list1.extend(collatz(n//divisor,multiplier,divisor))
else:
list1.extend(collatz(n*multiplier+1,multiplier,divisor))
return list1
#driver function to get the input number, multiplier and divisor
if __name__=="__main__":
n=int(input("Enter any positive integer N: "))
multiplier=int(input("Enter the multiplier: "))
divisor=int(input("Enter the divisor: "))
print("\n",collatz(n,multiplier,divisor))

You will get a stack overflow error after ~1000 recursions I believe.The way to override this: https://stackoverflow.com/a/3323013/10875953. But 2^64 is immensly large, you won't be able to do that. Rather try making a iterative function.

You should use your divisor parameter in the condition that select the operation on the number.
To make this easier to manage, you shouldn't use recursion. A simple while loop will suffice:
def collatz(n,divs=2,mult=3,inc=1,maxSize=-1):
result = []
while n not in result and len(result)!=maxSize:
result.append(n)
n = (n*mult+inc)if n%divs else n//divs
return result + ['...']*(n not in result)
output:
print(collatz(20))
[20, 10, 5, 16, 8, 4, 2, 1]
print(collatz(30,2,6,2,maxSize=10))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, '...']
print(collatz(30,2,6,2))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, 53, 320, 160, 80,
40, 20, 10, 5, 32, 16, 8, 4, 2, 1]
print(collatz(30,2,5,3))
[30, 15, 78, 39, 198, 99, 498, 249, 1248, 624, 312, 156]
print(collatz(60,4,3,-2,maxSize=10))
[60, 15, 43, 127, 379, 1135, 3403, 10207, 30619, 91855, '...']

Related

How to split an array according to conditional statement?

I have a time signal (37913 ms to 40010) and I want to split it in every 20 ms gap.
For example for first 20 ms:
for t in time:
if t>=37913 and t< 37933:
list1.append(t)
This gives me the list [37913.496549, 37916.878267, 37918.506757].
I want to make several different lists on every 20 ms gap. I know it should be really simple but somehow I cannot think of a solution.
****Edited****
So to further explain my point, what I actually want to achieve is that, have an incoming acceleration (with no upper time limit) signal (Green Bars) and I want to check if these incoming samples are in the range 0-20 ms, 10-30 ms or 20-40 ms and so on. If they are in such an interval then I have to approximate points (black dots) using this data. for example, if current values are in between 0-20 ms then I can use all of these values to approximate value at 10 ms by some approximation (let's suppose there is no current value at 10 ms). Approximation method is not important right now, I just want to capture these values
Any help or suggestion is highly appreciated thanks to all in advance.
Here's something that I think does what you desire. To test it I had to create some sample input data since you don't have any in your question—which is what is going on at the very beginning of the snippet.
For each time range interval, it creates a separate "bucket" which contains the corresponding time values within the time range. Note that some time values may end up being place in two buckets since the interval ranges overlap.
from pprint import pprint
import random
random.seed(42) # Create same "random" sequence each run for testing.
# First create some test data.
INTERVAL = 0.02 # 20 ms
start = 37913
times = []
nvalues = 2, 3, 4, 2, 1 # Number of values in each interval.
for i, nvalue in enumerate(nvalues):
lowerbounds = start + i*INTERVAL
upperbounds = start + (i+1)*INTERVAL
for _ in range(nvalue):
times.append(random.uniform(lowerbounds, upperbounds))
print('There are {} time values:'.format(len(times)))
times.sort() # Put into ascending order.
pprint(times)
#=======
# Split the times up into "buckets" of values depending on their range.
HALF_INTERVAL = INTERVAL / 2
brackets = []
print()
print('Time brackets:')
st = int(min(times))
for i in range(4):
begin = round(st + i*HALF_INTERVAL, 6)
end = round(begin + INTERVAL, 6)
brackets.append((begin, end))
print(' ', begin, end)
buckets = [[] for _ in range(len(brackets))] # Create empty buckets.
for t in times: # Put each time in the cooresponding bucket of times.
for i, (begin, end) in enumerate(brackets):
if begin <= t <= end:
buckets[i].append(t)
print()
print('Stored in corresponding interval bucket:')
for i, bracket in enumerate(brackets):
print('bucket[{}]: range: {!r}, values: {}'.format(
i, bracket, buckets[i]))
Sample Output:
There are 12 time values:
[37913.0005002151,
37913.01278853597,
37913.02446421476,
37913.025500586366,
37913.03472942428,
37913.04173877665,
37913.048438436395,
37913.05353398975,
37913.05784359135,
37913.060595944386,
37913.064372759494,
37913.09010710576]
Time brackets:
37913.0 37913.02
37913.01 37913.03
37913.02 37913.04
37913.03 37913.05
Stored in corresponding interval bucket:
bucket[0]: range: (37913.0, 37913.02), values: [37913.0005002151, 37913.01278853597]
bucket[1]: range: (37913.01, 37913.03), values: [37913.01278853597, 37913.02446421476, 37913.025500586366]
bucket[2]: range: (37913.02, 37913.04), values: [37913.02446421476, 37913.025500586366, 37913.03472942428]
bucket[3]: range: (37913.03, 37913.05), values: [37913.03472942428, 37913.04173877665, 37913.048438436395]
If your list of timestamps is sorted:
from itertools import groupby
sample = range(100)
INTERVAL_SIZE = 20
key = lambda x: x // INTERVAL_SIZE
list(list(v) for k, v in groupby(sample, key=key))
Will give you:
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
]
If it is not sorted, add a sort (by key) before you use groupby, since it requires a sorted iterable.
I would suggest this method:
arrs = []
while len(arr) > 20:
pice = arr[:20]
arrs.append(pice)
arr = arr[20:]
arrs.append(arr)
you can make function out of it which gives you a piece each time you call it.
If I understand your question, you could use a range:
r=range(37913,40010,20)
output:
[37913, 37933, 37953, 37973, 37993, 38013, 38033, 38053, 38073, 38093,
38113, 38133, 38153, 38173, 38193, 38213, 38233, 38253, 38273, 38293,
38313, 38333, 38353, 38373, 38393, 38413, 38433, 38453, 38473, 38493,
38513, 38533, 38553, 38573, 38593, 38613, 38633, 38653, 38673, 38693,
38713, 38733, 38753, 38773, 38793, 38813, 38833, 38853, 38873, 38893,
38913, 38933, 38953, 38973, 38993, 39013, 39033, 39053, 39073, 39093,
39113, 39133, 39153, 39173, 39193, 39213, 39233, 39253, 39273, 39293,
39313, 39333, 39353, 39373, 39393, 39413, 39433, 39453, 39473, 39493,
39513, 39533, 39553, 39573, 39593, 39613, 39633, 39653, 39673, 39693,
39713, 39733, 39753, 39773, 39793, 39813, 39833, 39853, 39873, 39893,
39913, 39933, 39953, 39973, 39993]
If you don't mind using numpy, you can try this:
import numpy as np
#a = np.arange(37913,40010+1,1)
a = np.arange(0,10+1,1) # comment this and uncomment above for your case
nlen = 2
parts = np.array_split(a,int(np.ceil(len(a) / nlen)))
print("a = {}".format(a)) # [ 0 1 2 3 4 5 6 7 8 9 10]
print("len(a) = {}".format(len(a))) # 11
print("parts[0] = {}".format(parts[0])) # [0 1]
print("parts[-1] = {}".format(parts[-1])) # [10]
If you just want to break whole array into n parts, just do this:
nparts = 6
np.array_split(a,nparts)

for loop based on dynamic list python

Edit:
The desired behaviour of the program is to find the number sequences that have an increasing trend, so I want to generate from ks list a list like this:
desiredList=[[97,122],[98,111],[98,101,103,103,104]]
I have the following, my goal is to run the for loop based on the length of the list, the list length gets changed inside the for loop itself. Python takes into account only the length before the for loop, when the length of the list is changed in the loop it still takes the older value before the loop. Here is the code:
ks=[97,122,111,98,111,98,101,103,103,104,97]
splitLine=2
counter=[]
for i in range(0,len(ks)):
a=ks[i:splitLine]
while len(a)>1:
for j in range(0,len(a)):
m=j
n=j+1
if(a[m]-a[n]<=0):
c=c+1
k=splitLine+c-1
a.append(ks[k]) #When append happens, the for loop still takes the older value of len(a) instead of new value
else:
a.pop(-1)
counter.append(a)
splitLine=splitLine+1
a=[]
break
A quick fix for your looping problem would be to swap out your for loop for a while loop. Change this:
for j in range(0,len(a)):
# <loop contents>
to this:
j = 0
while j < len(a):
# <loop contents>
j += 1
The for loop is grabbing values of j out of a range (a list in Python 2, and a generator object in Python 3). This range is calculated when the for loop is run the first time; it will not update after that, no matter what you do to a.
The while loop gives you more control in this situation, because you can specify the condition under which you want to exit the loop.
Your implementation is probably nesting too many loops for the problem it is trying to solve.
This first implementation contains an error. See below for the fix.
Try something along these lines perhaps:
l = [97,122,111,98,111,98,101,103,103,104,97]
out = []
acc = []
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
if len(acc) > 1:
out.append(acc)
acc = [v]
print(out)
>>>[[97, 122], [98, 111], [98, 101, 103, 103, 104]]
That previous code is slow and can drop the last found fragment. I found that error while running random tests on it to try an optimized version. The following code shows the original code with the correction and the optimized version which can be 30% faster.
def original(l):
out = []
acc = []
added = False
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
added = False
acc = [v]
if acc is not None and len(acc)>1 and not added:
added = True
out.append(acc)
return out
def optimized(l):
out = []
acc = None
tmp = None
deb_v = False
for v in l:
prev = acc[-1] if (acc is not None and len(acc)) else tmp
if prev is not None and v >= prev:
if tmp is not None:
acc = []
acc.append(tmp)
out.append(acc)
tmp = None
acc.append(v)
else:
acc = None
tmp = v
return out
# The original test data
l = [97,122,111,98,111,98,101,103,103,104,97]
assert original(l) == optimized(l) == [[97,122],[98,111],[98,101,103,103,104]]
# A list that triggered last-fragment-dropped error
l = [57, 16, 6, 19, 40, 3, 4, 13, 2, 70, 85, 65, 32, 69, 54, 51, 95, 74, 92, 46, 45, 26, 0, 61, 99, 43, 67, 71, 97, 10, 18, 73, 88, 47, 33, 82, 25, 75, 93, 80, 23, 37, 87, 90, 49, 15, 35, 63, 17, 64, 5, 72, 89, 21, 50, 8, 41, 86, 31, 78, 52, 76, 56, 42, 77, 36, 11, 60, 39, 22, 68, 27, 24, 28, 59, 96, 29, 38, 12, 79, 53, 9, 83, 94, 34, 14, 7, 48, 30, 20, 66, 62, 91, 58, 81, 1, 98, 44, 55, 84]
assert original(l) == optimized(l)
# Random testing
import random
l = list(range(100))
random.shuffle(l)
assert original(l) == optimized(l)
# Timing!
import timeit
print(timeit.timeit("original(l)", globals={"l":l, "original": original}))
# 43.95869998800117
print(timeit.timeit("optimized(l)", globals={"l":l, "optimized": optimized}))
# 34.82134292599949
As Moinuddin says, the root of your problem isn't clear to us. However, the code below shows how you can keep iterating over a list as its length changes:
def iterate_for_static_list_length(l):
for i in range(len(l)):
yield i
l.append(object())
def iterate_for_dynamic_list_length(l):
for i, _ in enumerate(l):
yield i
l.append(object())
if __name__ == '__main__':
l = [object()] * 3
print('Static implementation')
for value in iterate_for_static_list_length(l):
input(value)
print('\nDynamic implementation')
for value in iterate_for_dynamic_list_length(l):
input(value)
Output
Static implementation
0
1
2
Dynamic implementation
0
1
2
3
4
5
6
7
8
This program will keep going forever. In your code I can see that you conditionally append to the list within the loop, so it seems like it should terminate eventually.

Infinite Recursion in Python Function if Argument Too Long

I wrote this recursive function that returns the largest value in a list of integers:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
return l[0] if max_r(l[1:]) < l[0] else max_r(l[1:])
The call max_r([1,4,3,2,3,4,89,2,30,1] returns 89, but calling the function on a longer list:
max_r([96, 84, 87, 81, 94, 74, 65, 42, 45, 76, 5, 37, 86, 8, 46, 54, 62, 63, 35, 85, 16, 23, 18, 57, 51, 90, 58, 33, 47, 10, 64, 49, 67, 29, 71, 30, 9, 99, 75, 3, 97, 32, 59, 25, 27, 72, 61])
results in infinite recursion. Why?
It isn't infinitely recursive, but you are doing the same recursive call twice when you don't need to. Seems to complete quickly enough with the following change:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
result = max_r(l[1:])
return l[0] if result < l[0] else result
This isn't just a matter of calling the function recursively twice as many times, I'm not sure on the exact growth rate but it seems be exponential since each extra recursive call will be making more extra recursive calls.

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])

finding multiples of a number in Python

I'm trying to write a code that lets me find the first few multiples of a number. This is one of my attempts:
def printMultiples(n, m):
for m in (n,m):
print(n, end = ' ')
I figured out that, by putting for m in (n, m):, it would run through the loop for whatever number was m.
def printMultiples(n, m):
'takes n and m as integers and finds all first m multiples of n'
for m in (n,m):
if n % 2 == 0:
while n < 0:
print(n)
After multiple searches, I was only able to find a sample code in java, so I tried to translate that into python, but I didn't get any results. I have a feeling I should be using the range() function somewhere in this, but I have no idea where.
If you're trying to find the first count multiples of m, something like this would work:
def multiples(m, count):
for i in range(count):
print(i*m)
Alternatively, you could do this with range:
def multiples(m, count):
for i in range(0,count*m,m):
print(i)
Note that both of these start the multiples at 0 - if you wanted to instead start at m, you'd need to offset it by that much:
range(m,(count+1)*m,m)
Does this do what you want?
print range(0, (m+1)*n, n)[1:]
For m=5, n=20
[20, 40, 60, 80, 100]
Or better yet,
>>> print range(n, (m+1)*n, n)
[20, 40, 60, 80, 100]
For Python3+
>>> print(list(range(n, (m+1)*n, n)))
[20, 40, 60, 80, 100]
Based on mathematical concepts, I understand that:
all natural numbers that, divided by n, having 0 as remainder, are all multiples of n
Therefore, the following calculation also applies as a solution (multiples between 1 and 100):
>>> multiples_5 = [n for n in range(1, 101) if n % 5 == 0]
>>> multiples_5
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
For further reading:
https://www.mathsisfun.com/definitions/natural-number.html
https://www.mathwizz.com/arithmetic/help/help9.htm
https://www.calculatorsoup.com/calculators/math/multiples.php
For the first ten multiples of 5, say
>>> [5*n for n in range(1,10+1)]
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
You can do:
def mul_table(n,i=1):
print(n*i)
if i !=10:
mul_table(n,i+1)
mul_table(7)
If this is what you are looking for -
To find all the multiples between a given number and a limit
def find_multiples(integer, limit):
return list(range(integer,limit+1, integer))
This should return -
Test.assert_equals(find_multiples(5, 25), [5, 10, 15, 20, 25])
Another method that can be done is trying to make a list. Here's my example for getting the first 20 multiples of 7.
Input:
multiples_7 = [x * 7 for x in range(1,21)]
print(multiples_7)
Output:
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 105, 112, 119, 126, 133, 140]
def multiples(n,m,starting_from=1,increment_by=1):
"""
# Where n is the number 10 and m is the number 2 from your example.
# In case you want to print the multiples starting from some other number other than 1 then you could use the starting_from parameter
# In case you want to print every 2nd multiple or every 3rd multiple you could change the increment_by
"""
print [ n*x for x in range(starting_from,m+1,increment_by) ]
For first 10 multiples of 5 you can do as
import numpy as np
#np.arange(1,11) array from 1 to 10
array_multipleof5 = [5*n for n in np.arange(1,11)]
array_multipleof5 = np.array(array_multipleof5)
print(array_multipleof5)
How to calculate the first n multiples of a given number x, in the compact python's lambda notation
n_multiples_of_x = lambda n,x : list( range(x, x*n + 1, x) )
Tests:
assert n_multiples_of_x(5, 5) == [5, 10, 15, 20, 25]

Categories