Issue with collatz conjecture in python, loop ends far too early - python

I wrote a program to check the highest number of steps of the collatz conjecture in a range. However, the number I get is incorrect, it appears that the loop ends far too early. Can anyone tell me what is wrong with my code? I checked multiple times over different ranges and each time it is still wrong.
def collatz_sequence(n):
sequence = []
while n != 1:
sequence.append(n)
if n % 2 == 0:
n = n // 2
elif n % 2 != 0:
n = 3 * n + 1
return sequence
max_len = 1
for i in range(1, 1000):
if len(collatz_sequence(i)) > max_len:
max_len = i
print(max_len)

You are setting max_len to the index of the longest sequence, not its length. You want to keep track of the length of the longest sequence seen so far (and update it for longer sequences as they are seen), rather than its position in the first 1000 sequences. Try:
def collatz_sequence(n):
sequence = []
while n != 1:
sequence.append(n)
if n % 2 == 0:
n = n // 2
elif n % 2 != 0:
n = 3 * n + 1
return sequence
max_len = 1
for i in range(1, 1000):
seq_len = len(collatz_sequence(i))
if seq_len > max_len:
max_len = seq_len
print(i, max_len)
3 7
6 8
7 16
9 19
18 20
25 23
27 111
54 112
73 115
97 118
129 121
171 124
231 127
313 130
327 143
649 144
703 170
871 178

you want to store len(collatz_sequence(i)) in max_len:
max_len = 1
for i in range(1, 1000):
l = len(collatz_sequence(i))
if l > max_len:
max_len = l
print(i, max_len)

Related

Compacting Number values into shorter string

I have 1296 random values ranging 0-31. I want to represent this information so let’s say I just concatenate all 1296 values into a string. If I were to do that the string would be about 2500 characters long. How can I store this information in a shorter string so I can still know all 1296 values in the correct order but not have such a long string? (I managed to get it to 648, but wanted to see if someone has an even better way)
This will work when the range of numbers in the input list are 0-31 (inclusive) and when the list length is a multiple of 3
import random
numbers = [random.randint(0, 31) for _ in range(1296)]
def pack(n):
result = []
for i in range(0, len(n), 3):
result.append(n[i] << 10 | n[i+1] << 5 | n[i+2])
return ''.join(map(chr, result))
def unpack(s):
result = []
for o in map(ord, s):
for shift in 10, 5, 0:
result.append(o >> shift & 0x1F)
return result
packed = pack(numbers)
print(len(packed))
result = unpack(packed)
assert result == numbers
Output:
432
Note:
If the range of numbers was 1-31 then this technique (with a minor modification) could be used for any list length because zero could be used as a padding indicator as follows:
import random
numbers = [random.randint(1, 31) for _ in range(1295)]
def pack(n):
result = []
a = None
for i, x in enumerate(n):
match i % 3:
case 0:
a = x << 10
case 1:
a |= x << 5
case _:
result.append(a|x)
a = None
if a is not None:
result.append(a)
return ''.join(map(chr, result))
def unpack(s):
result = []
for o in map(ord, s):
for shift in 10, 5, 0:
if (n := o >> shift & 0x1F) == 0:
break
result.append(n)
return result
packed = pack(numbers)
print(len(packed))
result = unpack(packed)
assert result == numbers
You can easily store 32 unique values in one character, which means your 1296 numbers can fit in a string of 1296 characters.
For example:
import random
numbers = [random.randint(0, 31) for i in range(1296)]
def numbers_to_string(numbers):
return "".join(chr(ord("0") + number) for number in numbers)
def numbers_from_string(string):
return [ord(char) - ord("0") for char in string]
numbers_str = numbers_to_string(numbers)
numbers_roundtrip = numbers_from_string(numbers_str)
print(numbers_roundtrip == numbers)
Output:
True
These are the numbers and the characters used to represent them:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 :
11 ;
12 <
13 =
14 >
15 ?
16 #
17 A
18 B
19 C
20 D
21 E
22 F
23 G
24 H
25 I
26 J
27 K
28 L
29 M
30 N
31 O

Appending a list in this situation

This is the question,
Write a Python program to find numbers in between 100 and 400 (both inclusive) where each digit of
the numbers is an even number. The numbers obtained should be printed in a comma-separated
sequence. I'm only allowed to use while loops so I'm not sure how.
Edited: It has to in a single line
My current code:
x = 100
while x < 400:
x += 2
str_x = str(x)
if int (str_x[0]) % 2 == 0 and int(str_x[1]) % 2 == 0 and int(str_x[2]) % 2 == 0:
l = [str_x]
print(l)
You need to collect the values in a list, and at the end print them comma-separated
x = 100
keep = []
while x <= 400:
strx = str(x)
if all(int(digit) % 2 == 0 for digit in strx):
keep.append(strx)
x += 2
print(",".join(keep))
# 200,202,204,206,208,220,222,224,226,228,240,242,244,246,248,260,262,264,266,268,280,282,284,286,288,400
Here is a solution with only while loops and without converting to string:
i = 100
while i <= 400:
d = i
flag = True
while d > 0:
d,r = divmod(d,10)
if r%2:
flag = False
if flag:
print(i)
i += 2 # optimization as only even numbers are valid
output (as single line):
200 202 204 206 208 220 222 224 226 228 240 242 244 246 248 260 262 264 266 268 280 282 284 286 288 400
This is just some correction to have a list with all the values at the end. With only using a while loop.
x = 100
l = []
while x < 400:
x += 2
str_x = str(x)
if int(str_x[0]) % 2 == 0 and int(str_x[1]) % 2 == 0 and int(str_x[2]) % 2 == 0:
l.append(x)
print(l)
A more pythonic way in doing this:
l = lambda a, b : [
x for x in range(a, b+1)
if all(int(digit) % 2 == 0 for digit in str(x))
]
print(l(200, 401))
Cant you just do :
x= 100 # start number
while x <= 400:
odds = ['1','3','5','7','9'] # odds
t = len(str(x)) # how long string is
v = 0 # a variable
for i in list(str(x)): # a for loop
if i not in odds:# checking if its even or not
v += 1 # adds 1 to v
else:
v += 0
if v == t: #if its all even,
print(x, end = ',') #print it out.
x += 1 # move on to next one
#200,202,204,206,208,220,222,224,226,228,240,242,244,246,248,260,262,264,266,268,280,282,284,286,288,400,

Finding the n-th number that consists of only 2 or 3

I am really struggling with this program. I would appreciate any kind of help.
For a natural number we say that it is strange if it is completely composed of digits 2 and 3. The user enters a natural number. The program prints the n-th strange number.
Numbers that are considered strange are 2, 3, 22, 23, 33...
n = int(input())
current_number = 1
counter_strange = 0
counter = 0
while counter_strange < n:
x = current_number
while x < n:
k = x % 10
if k != 2 or k != 3:
counter += 1
else:
break
if counter >= 1:
counter_strange += 1
current_number += 1
print(current_number-1)
Strange numbers come in blocks. A block of 2 1-digit numbers, followed by a block of 4 2-digit numbers, then 8 3-digit numbers. You can do the math and determine which block of k-digit numbers a given index n is, and how far into that block it lies. Convert that distance into a base-2 number, zero-filled to k digits, then replace 0 by 2 and 1 by 3. Convert the result back to an int:
from math import log2, ceil
def strange(n):
"""returns the nth strange number"""
#first determine number of digits:
k = ceil(log2(n+2)) - 1
#determine how far is in the block of strange k-digit numbers
d = n - (2**k - 1)
#convert to base 2, zfilling to k digits:
s = bin(d)[2:].zfill(k)
#swap 2,3 for 0,1:
s = s.replace('0','2').replace('1','3')
#finally:
return int(s)
for n in range(1,10): print(n,strange(n))
Output:
1 2
2 3
3 22
4 23
5 32
6 33
7 222
8 223
9 232
You can use a while loop with itertools.product in a generator function. Using a generator will allow you to create a stream from which you can access strange numbers on the fly:
import itertools
def strange():
c = 0
while True:
yield from map(''.join, itertools.product(*([['2', '3']]*(c:=c+1))))
s = strange()
for _ in range(10):
print(int(next(s)))
Output:
2
3
22
23
32
33
222
223
232
233

The output is 100 but i don't understand why

The output is 100 but i don't understand why. Can someone give some insight into how this code works step by step.
def fun(n):
if (n > 100):
return n - 5
return fun(fun(n+11))
print(fun(45))
Decompose your code.
def fun(n):
if (n > 100):
return n - 5
return fun(fun(n+11))
print(fun(45))
> n =45, n > 100 == False, return fun(fun(n + 11))
> fun(45+11) == fun(56), n = 56, 56 > 11 == False, return fun(fun(n + 11))
> fun(56+11), n = 67
> fun(67+11), n = 78
> fun(78+11), n = 89
> fun(89 + 11), n= 100
> fun(100+11), n = 111
> fun(111), n > 100 return 111 - 5
> fun(106), return 106 - 5
> fun(101), return 101 - 5
> fun(96), return fun(fun(96+11))
> fun(107), return 107 - 5
> fun(102), return 102 - 5
> fun(97), return fun(fun(97+11))
> 108 - 5
> 103 - 5
> fun(98)
> 109 - 5
> 104 - 5
> fun(99)
> 110 - 5
> 105 - 5, return 100
And here with code:
def fun(n, s):
space = " " * s
if (n > 100):
print(f"{space} > return {n}-5={n-5}")
return [n - 5, s - 3]
print(f"{space} > return fun(fun({n} + 11))\n {space + ' '} > return fun(fun({n+11}))")
return fun(*fun(n+11, s+3))
print(fun(45, 0))
As it has been pointed in some comments, your best chance is to write down what is going on on paper:
When n is lower than 100, you add 11 to n, and go deeper.
When n is higher than 100, you return n-5, and go higher.
If you want to represent this on paper, you can go down each time you add 11, and return up each time you subtract 5. You'll have your result when surpass your starting point, going up.
START 100
| ^
V |
45 99 99 105
56 98 98 104 110 110
67 97 97 103 109 109
78 96 96 102 108 108
89 101 107 107
100 106
111 111
Since you're experimenting with double recursion, I suppose you're already familiar with recursion; if not, have a look there first!

'int' object is not subscriptable on second iteration of loop when trying to convert a string item in a list to a list of integers

I'm new to Python and I'm pretty confused as to why this part of my program is causing problems.
n = int(input())
c = []
l = []
i = 0
while(i<n):
z = int(input())
c.append(z)
x = str(input())
l.append(x)
i += 1
listCount = 0
while(listCount < n ):
binNumber = c[listCount]
dataList = [int(n) for n in l[listCount].split()]
dataList.sort()
listLength = len(dataList)
lowestData = dataList[0]
l = lowestData
while( l <= dataList[listLength-1]):
intervalCounter = 0
traverseCounter = 0
while(traverseCounter < listLength):
if(dataList[traverseCounter] >= l and dataList[traverseCounter] <= l + binNumber - 1):
intervalCounter += 1
traverseCounter += 1
else:
traverseCounter += 1
print( str(l) +'-'+ str(l + binNumber - 1) + ' ' + str(intervalCounter))
l = l + binNumber
print('\b')
listCount += 1
The program outputs correctly on the first iteration of the loop but on the second or more iteration of the loop, it gives me this
Traceback (most recent call last):
File "solution.py", line 21, in <module>
dataList = [int(n) for n in l[listCount].split()]
TypeError: 'int' object is not subscriptable
Does first iteration of the loop change the code in any way to make it behave like this, I'm really confused as to how this works for the first loop but not the second.
The inputs are n test cases followed by 2 lines each first being the intervals and the second line being the values
input
2
10
36 25 38 46 55 68 72 55 36 38 67 45 22 48 91 46 52 61 58 55
50
2 2 1 255 0 3 4 5 6 7 200 100 10 50 0
This is how the output is supposed to be:
22-31 2
32-41 4
42-51 4
52-61 6
62-71 2
72-81 1
82-91 1
0-49 11
50-99 1
100-149 1
150-199 0
200-249 1
250-299 1
But all I get is:
22-31 2
32-41 4
42-51 4
52-61 6
62-71 2
72-81 1
82-91 1

Categories

Resources