Resetting Collatz Counter on Each New Recursion - python

I made a code that measure number of steps it takes to return to 1 in a Collatz Conjecture. Here Is my code
counter = 0
def collatz(n):
global counter
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n)
elif n % 2 == 0 :
n = n/2
return collatz(n)
print(collatz(9921615699))
print(collatz(9921615699))
I expect the Last two print commands to print 311 and 311. Instead, they print 311 and 622. I guess that was easy enough to see in the code what is wrong. How can i fix that? how can counter reset each time a command is completed and not when the function is run.

Instead of using global variable you could make the counter a parameter with default value:
def collatz(n, counter=0):
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n, counter)
elif n % 2 == 0 :
n = n/2
return collatz(n, counter)

You're using recursion, so just use it properly:
def collatz(n, counter=0):
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n, counter)
elif n % 2 == 0 :
n = n/2
return collatz(n, counter)
print(collatz(9921615699))
print(collatz(9921615699))
You were using global in your original version which is usually not what you want to do. You got to see first-hand why.
You could have reset the counter, e.g.
result = counter
counter = 0
return result
But that's pretty nasty, let's not do that. When you're implementing a recursive algorithm there's probably no good reason to have a global variable.

Related

How to show that using 'cache' in a Python program is more efficient?

The program uses two ways to calculate the Collatz Conjecture sequence, I want to show that the method using cache, rather than starting from scratch is more efficient - uses fewer processes. I have tried using the time module and it keeps giving inconsistent results - sometimes the cache method appears to be slower than calculating the sequence from scratch.
cache = {}
def in_cache(n):
if n in cache.keys():
return True
else:
return False
def calculate_collatz(n):
count = 0
collatz_sequence = "N: " + str(n)
while n != 1:
if n % 2 == 0:
n = n // 2
else:
n = (3 * n) + 1
count += 1
collatz_sequence += " -> {}".format(n)
return (collatz_sequence, count)
def collatz(n):
if in_cache(n):
print("Using cache...")
return cache[n]
else:
print("Calculating from scratch...")
result = calculate_collatz(n)
cache[n] = result
return result
choice = ""
while choice != 'Q':
#print('The cache: ', cache)
choice = input("Enter an integer to calculate the Collatz Conjecture sequence. Enter Q to quit\n")
try:
n = int(choice)
print(collatz(n))
except:
print("You did not enter a whole number")

Annotating adjacent values in a list

It's supposed to be a roll of dice (random) then adjacent values (runs) are supposed to be in ( ). One challenge is using the current - 1 with the 0 index (think I got that resolved by using range(len(dieRun) -1). But another challenge is using 'current + 1' as it tends to 'out of range' errors.
One thought I have is to maybe build a function to compare the values for adjacents? Then use whatever return I get from that to reference a variable, then use that variable in a formatted Print of the dieRun? But, I don't see how that would be better as then I'd still have to figure out how to place that variable as a "(" or ")" with the print(dieRun) list.
Still a newb.
def main():
from random import randint
counter = 0
inRun = 0
dieRun = []
while counter < 20:
roll = randint(0,6)
dieRun.append(roll)
counter = counter +1
index = 0
counter = 0
value = 0
inRun == False
print(dieRun) # just to see what I'm working with
while counter < len(dieRun):
for i in range(0, len(dieRun)-1):
if dieRun[i] != dieRun[i-1]:
print(")" , end= "")
inRun = False
counter = counter + 1
if dieRun[i] == dieRun[i+1]:
inRun = True
print("(")
counter = counter + 1
print(dieRun[i])
if inRun:
print("(")
if inRun :
print(")", end="")
main()
if you want the output like: 1(3 3) 4 5 (6 6 6) 2 1 3 (2 2) 1 (4 4) 5 6 1 2
from random import randint
dieRun = []
for i in range(20):
roll = randint(0,6)
dieRun.append(roll)
inRun = False
print(dieRun)
for i, n in enumerate(dieRun):
if i < len(dieRun)-1:
if not inRun:
if n == dieRun[i+1]:
print('(', n,' ', end='')
inRun = True
else:
print(n,' ', end='')
else:
if n != dieRun[i+1]:
print(n, ')',' ', end='')
inRun = False
else:
print(n,' ', end='')
else:
if dieRun[i-1] == n:
print(n, ')')
else:
print(n)
just a thought, hope it help.
Just a quick fix, I have commented in the code.
For the inline print, you have provided the solution 'print(")" , end= "")', but I don't know why you didn't make it for every print().
if dieRun[i] != dieRun[i-1]: # will check the first number with last number
dieRun[0] != dieRun[-1]: # [-1] is the last item in the list
if dieRun[i] == dieRun[i+1]: # will index out of length
dieRun[len(..)-1] != dieRun[len(..)]: # [len(..)] wil be out of range
Since the head and tail of the list will always cause problem, I just chopped them off from the for loop, and do it manually.
There must be some cleaner solution:
from random import randint
counter = 0
inRun = 0
dieRun = []
while counter < 20:
roll = randint(0,6)
dieRun.append(roll)
counter = counter +1
index = 0
counter = 0
value = 0
inRun == False
print(dieRun)
# while counter < len(dieRun): # while loop is redundant with the for loop
print('(', dieRun[0],' ', end='') # print the first number manually
for i in range(1, len(dieRun)-1):
if dieRun[i] != dieRun[i-1]:
print(")(" , end= "") # change ")" to ")("
inRun = False
counter = counter + 1
"""
these are redundant, just like if True, don't need elif not True
# if dieRun[i] == dieRun[i+1]:
# inRun == True
# print("(", end='')
# counter = counter + 1
"""
print(dieRun[i],' ', end='')
print(dieRun[-1],')', end='') # print the last number manually

Need to ignore leading zeros for digit count function in python

.123 is converted to 0.123 as a string so my count comes out at (0,0,1) instead of (0,0,0). I need to ignore that leading 0 but I can't figure out how.
def digit_count(n):
n=str(int(n))
even_count=0
odd_count=0
zero_count=0
for i in n:
if int(i)%10 ==0:
zero_count +=1
elif int(i) % 2 ==0:
even_count += 1
elif int(i) %2 !=0:
odd_count +=1
return(even_count,odd_count,zero_count)
One hacky solution:
def digit_count(n):
if isinstance(n, float) and str(n).split('.')[0]=='0':
return (0,0,0)
else:
n=str(int(n))
even_count=0
odd_count=0
zero_count=0
for i in n:
if int(i)%10 ==0:
zero_count +=1
elif int(i) % 2 ==0:
even_count += 1
elif int(i) %2 !=0:
odd_count +=1
return(even_count,odd_count,zero_count)
def digit_count( n ) :
## convert number to string
n = str( int(n))
## declare counts
even_count, zero_count = 0,0
for i in n :
i = int(i)
## case when n = 0.1231
if len(n) == 1 and i == 0:
return (0,0,0)
## case when n contains 0
elif i == 0:
zero_count += 1
## case when n contains even
elif i != 0 and i%2 == 0 :
even_count += 1
return ( even_count, len(n) - even_count- zero_count, zero_count )
digit_count( 123059.9 )
>> (1,4,1)
digit_count( 0.123 )
>> (0,0,0)
how about something like this for a python 3 solution?
def digit_count(n):
n=list(str(int(n))); #turn into a list array
if n[-1] == "0": #get the first item (leading zeroes).
n[-1] = ""; #delete it.
n=''.join(n); #rejoin as a new string.
even_count = odd_count = zero_count = 0; #I cleaned this up too.
for i in n:
if int(i)%10 == 0:
zero_count += 1
elif (int(i) % 2 == 0) ^ (int(i) %2 == 0): #I cleaned this up I hope you don't mind.
even_count += 1
return(even_count,odd_count,zero_count)
print(digit_count(.123));

How do I run a binary search for words that start with a certain letter?

I am asked to binary search a list of names and if these names start with a particular letter, for example A, then I am to print that name.
I can complete this task by doing much more simple code such as
for i in list:
if i[0] == "A":
print(i)
but instead I am asked to use a binary search and I'm struggling to understand the process behind it. We are given base code which can output the position a given string. My problem is not knowing what to edit so that I can achieve the desired outcome
name_list = ["Adolphus of Helborne", "Aldric Foxe", "Amanita Maleficant", "Aphra the Vicious", "Arachne the Gruesome", "Astarte Hellebore", "Brutus the Gruesome", "Cain of Avernus"]
def bin_search(list, item):
low_b = 0
up_b = len(list) - 1
found = False
while low_b <= up_b and found == False:
midPos = ((low_b + up_b) // 2)
if list[midPos] < item:
low_b = midPos + 1
elif list[midPos] > item:
up_b = midPos - 1
else:
found = True
if found:
print("The name is at positon " + str(midPos))
return midPos
else:
print("The name was not in the list.")
Desired outcome
bin_search(name_list,"A")
Prints all the names starting with A (Adolphus of HelBorne, Aldric Foxe .... etc)
EDIT:
I was just doing some guess and check and found out how to do it. This is the solution code
def bin_search(list, item):
low_b = 0
up_b = len(list) - 1
true_list = []
count = 100
while low_b <= up_b and count > 0:
midPos = ((low_b + up_b) // 2)
if list[midPos][0] == item:
true_list.append(list[midPos])
list.remove(list[midPos])
count -= 1
elif list[midPos] < item:
low_b = midPos + 1
count -= 1
else:
up_b = midPos - 1
count -= 1
print(true_list)
Not too sure if this is what you want as it seems inefficient... as you mention it seems a lot more intuitive to just iterate over the entire list but using binary search i found here i have:
def binary_search(seq, t):
min = 0
max = len(seq) - 1
while True:
if max < min:
return -1
m = (min + max) // 2
if seq[m][0] < t:
min = m + 1
elif seq[m][0] > t:
max = m - 1
else:
return m
index=0
while True:
index=binary_search(name_list,"A")
if index!=-1:
print(name_list[index])
else:
break
del name_list[index]
Output i get:
Aphra the Vicious
Arachne the Gruesome
Amanita Maleficant
Astarte Hellebore
Aldric Foxe
Adolphus of Helborne
You just need to found one item starting with the letter, then you need to identify the range. This approach should be fast and memory efficient.
def binary_search(list,item):
low_b = 0
up_b = len(list) - 1
found = False
midPos = ((low_b + up_b) // 2)
if list[low_b][0]==item:
midPos=low_b
found=True
elif list[up_b][0]==item:
midPos = up_b
found=True
while True:
if found:
break;
if list[low_b][0]>item:
break
if list[up_b][0]<item:
break
if up_b<low_b:
break;
midPos = ((low_b + up_b) // 2)
if list[midPos][0] < item:
low_b = midPos + 1
elif list[midPos] > item:
up_b = midPos - 1
else:
found = True
break
if found:
while True:
if midPos>0:
if list[midPos][0]==item:
midPos=midPos-1
continue
break;
while True:
if midPos<len(list):
if list[midPos][0]==item:
print list[midPos]
midPos=midPos+1
continue
break
else:
print("The name was not in the list.")
the output is
>>> binary_search(name_list,"A")
Adolphus of Helborne
Aldric Foxe
Amanita Maleficant
Aphra the Vicious
Arachne the Gruesome
Astarte Hellebore

Python does not start for loop

old = [[0 for x in range(3)] for y in range(10)]
count =0
# check if the number has non-repeating digits
def different(number):
digit_list = [0] * 4
i = 0
while i:
digit_list[i] = number%10
number /= 10
i += 1
for x in range(0,3):
for y in range(x+1,3):
if digit_list[x] == digit_list[y]:
return False
return True
# save the tried numbers, plus and minus values
# for prediction of the next number
def save(number,plus,minus):
global count
old[count][0] = number
old[count][1] = plus
old[count][2] = minus
count += 1
return
# compare for plus values
def get_plus(number1,number2):
ret_value = 0
for x in range(0, 3):
if number1 % 10 == number2 % 10:
ret_value += 1
number1 /= 10
number2 /= 10
return ret_value
# compare for minus values
def get_minus(number1,number2):
temp = [[0]*4 for i in range(2)]
ret_value = 0
for x in range(0,3):
temp[0][x] = number1 % 10
temp[0][x] = number2 % 10
number1 /= 10
number2 /= 10
for x in range(0,3):
for y in range(0,3):
if x != y:
if temp[0][x] == temp[1][y]:
ret_value += 1
return ret_value
# compare the number to be asked with the numbers in the array
def control(number):
for x in range(0,count-1):
if get_plus(old[x][0],number) != old[x][1]:
return False
if get_minus(old[x][0],number) != old[x][2]:
return False
return True
def main():
flag = False
print('1023 ??')
plus = input('plus ?')
minus = input('minus ?')
save(1023, plus, minus)
print('4567 ??')
plus = input('plus ?')
minus = input('minus ?')
save(4567, plus, minus)
for i in range(1024, 9876):
if different(i):
if control(i):
print(i + ' ??')
plus = input('plus ?')
minus = input('minus ?')
save(i, plus, minus)
if plus == 4 and minus == 0:
print('I WON !!!')
flag = True
break
if not flag:
print('False')
return
main()
I am trying to make an AI for mindgame in python. But in this function it doesn't even start the for loop. Can anyone know why ?
The while loop in your different() function does nothing as while(0) will prevent the loop from running. Even if that would run, your different() function will always return false. At least in the last loop it will compare digit_list[3] == digit_list[3] as both loop range until 3. This is always true and the function will return false. Thus the code within your main loop will never be entered.
def different(number):
digit_list = [0] * 4
i = 0
while i:
digit_list[i] = number%10
number /= 10
i += 1
for x in range(0,3):
for y in range(x+1,3):
if digit_list[x] == digit_list[y]:
return False
return True
Try this one:
import random
def different(num):
digits = []
while num >= 1:
cur = num%10
if cur in digits:
return False
digits.append(cur)
num = (num - cur) / 10
return True
for i in range(0, 10000):
rand = random.randrange(1000, 10000)
if not different(rand):
print(rand)

Categories

Resources