Compacting Number values into shorter string - python

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

Related

Find Python Multiplication from user input

Junior Python Coder here! Trying to print multiplication table with user input but stuck.
min_num=1
max_num=11
starting_range = int(input ("Enter the minimum number: "))
ending_range = int(input ("Enter the maximum number: "))
print ("The Multiplication Table of: ", starting_range)
for count in range(1, 11):
print (starting_range, 'x', count, '=', ending_range * count)
if max_num - min_num > 10 :
print('invalid range ')
else:
for num in range (min_num,max_num):
print ("The Multiplication Table of: ", ending_range)
for count in range(min_num, max_num):
print (starting_range, 'x', count, '=', ending_range * count)
I'm not sure I really understand what you're trying to do as your code isn't formatted, but for a multiplication table, a nested loop is a solution.
The basic idea is: For every number in the given range, loop over the whole range and multiply it by each element. This will print the whole multiplication table.
start = 1 # You can change these to be inputted by the user.
end = 10
for i in range(start, end + 1): # We add 1 to the end because range() is exclusive on endpoint.
for j in range(start, end + 1):
print(f"{i} x {j} = {i * j}")
If you only need the table as something like:
15 x 1
15 x 2
15 x 3
...
You can do this with one loop:
num = 10
for i in range(1, num + 1):
print(f"{num} x {i} = {num * i}")
I recommend you search up on F-strings in Python if you do not understand the print(f"..") parts. They're very convenient. Good luck!
Minimal change to original code:
min_num = 1
max_num = 11
starting_range = 4 # You can use int() and input() as in your original code to make this user defined.
ending_range = 7
# Removed the error checking, wasn't sure precisely what it was meant to do.
for num in range(starting_range,ending_range):
print ("The Multiplication Table of: ", num)
for count in range(min_num, max_num):
print (num, 'x', count, '=', num * count)
Try it at https://www.mycompiler.io/view/7JVNtxkT53k
This prints:
The Multiplication Table of: 4
4 x 1 = 4
4 x 2 = 8
4 x 3 = 12
4 x 4 = 16
4 x 5 = 20
4 x 6 = 24
4 x 7 = 28
4 x 8 = 32
4 x 9 = 36
4 x 10 = 40
The Multiplication Table of: 5
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
5 x 6 = 30
5 x 7 = 35
5 x 8 = 40
5 x 9 = 45
5 x 10 = 50
The Multiplication Table of: 6
6 x 1 = 6
6 x 2 = 12
6 x 3 = 18
6 x 4 = 24
6 x 5 = 30
6 x 6 = 36
6 x 7 = 42
6 x 8 = 48
6 x 9 = 54
6 x 10 = 60
Alternative code defining a function:
def print_multiplication_table(n, max_value=11):
if n > max_value or n <= 0:
raise ValueError
print(f"The Multiplication Table of: {n}")
for m in range(1, max_value):
print(f"{n} x {m} = {n*m}")
starting_range = 4 # You can use int() and input() as in your original code to make this user defined.
ending_range = 7
for i in range(starting_range, ending_range):
print_multiplication_table(i)
Try it at https://www.mycompiler.io/view/1uttFLmFEiD

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

How to convert number to its corresponding alphabet

For example, 1 should give me alphabet a,
2 > b
3 > c
...
26 > a
And 27 should give an again
28 > b
.. Till 9999
The below code only works with 26 numbers
import string
di=dict(zip(string.letters,[ord(c)%32 for c in string.letters]))
di['c']
Output is 3.
Can anyone please help?
this would do
def num2char(num):
return chr(((num-1) % 26) + ord('a'))
you don't need to go through a dictionary to convert numbers to letters that way. You can apply the modulo to positions in a string of letters
letters = "abcdefghijklmnopqrstuvwxyz"
for i in range(1,1000):
print(i,">",letters[(i-1)%26])
output:
1 > a
2 > b
3 > c
4 > d
5 > e
6 > f
7 > g
8 > h
9 > i
10 > j
11 > k
12 > l
13 > m
14 > n
15 > o
16 > p
17 > q
18 > r
19 > s
20 > t
21 > u
22 > v
23 > w
24 > x
25 > y
26 > z
27 > a
28 > b
29 > c
...
See if either one of these is what you're looking for:
print (ord ('a') - 96)
print (chr (1 + 96))
Your data structure is odd. If your input alphabet contains 9999 symbols, you need a dictionary of that many elements. But actually you don't really need a dictionary at all, just the observation that the input number modulo 26 plus 1 maps to the character codes starting at a.
def strmap(numbers):
base = ord('a')
mapped = [chr(base + (n-1)%26) for n in numbers]
return ''.join(mapped)
Demo: https://ideone.com/FeVkO4

A single function to convert decimal values to binary, hexadecimal and octal does not convert to binary

I am trying to build a standard function to convert any decimal value to its octal, hexadecimal and binary equivalent but it doesn't work for binary for some reason. I tried putting extra precautions into the conditional statement to check for base 2 but it still did not work.
This is the function (I know that this doesn't work well with hexadecimal values. I am going to take care of that afterwards):
def convert(num, base):
remainder = num % base
conv = []
if(remainder == 0):
conv.append('0')
elif(remainder != 0 and base == 2):
conv.append('1')
else:
conv.append(str(remainder))
result = ''.join(conv)
output = result[::-1]
return int(output)
In the line elif(remainder != 0 and base == 2):, I am checking if the remainder isn't 0 and the base is 2 to add a 1 into the temporary conv list. I am then converting the list to a string, reversing it and returning it as an int.
For example. If the input is 17, the output needs to be this:
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 10 8 1000
9 11 9 1001
10 12 A 1010
11 13 B 1011
12 14 C 1100
13 15 D 1101
14 16 E 1110
15 17 F 1111
16 20 10 10000
17 21 11 10001
These are the functions that take care of the input and the printing:
def print_formatted(number):
# your code goes here
for i in range(number):
print(
str(i + 1) + " " +
str(convert(i + 1, 8)) + " " +
str(convert(i + 1, 16)) + " " +
str((convert(i + 1, 2)))
)
if __name__ == '__main__':
n = int(input())
print_formatted(n)
Update
Instead of going through the entire equation, I decided to go with the built-in functions and trim the first two characters (i.e 0b) so it fits the format well. I am trying to space them away from each other based on the width of the binary output but I couldn't figure out a way of doing that. This is what I have so far:
def convert(num, base):
# get the highest power
val = ''
hex_char_list = ['A', 'B', 'C', 'D', 'E', 'F']
if(base == 2):
bin_num = bin(num)
bin_list = list(bin_num)
bin_list_2 = bin_list[2:]
val = ''.join(bin_list_2)
if(base == 8):
oct_num = oct(num)
oct_list = list(oct_num)
oct_list_2 = oct_list[2:]
val = ''.join(oct_list_2)
if(base == 16):
hex_num = hex(num)
hex_list = list(hex_num)
hex_list_2 = hex_list[2:]
val = ''.join(hex_list_2)
if val in hex_char_list:
val = val.upper()
return val
def print_formatted(number):
# your code goes here
width = len(convert(number, 2).format(number))
for i in range(number):
print(
str(i + 1) + width +
str(convert(i + 1, 8)) + width +
str(convert(i + 1, 16)) + width +
str((convert(i + 1, 2)))
)
if __name__ == '__main__':
n = int(input())
print_formatted(n)
Your elif is superflous - if you do %2 the result can be only 0 or 1 - no need to do handle it differently.
Your code does not convert the whole number - you check the modulo of the number, not how often your base (and higher powers of your base) fits into it.
You need to get the highest possible power for your base that fits into your number. Then you need to get how often that one fits into your number, subtract it from the number and continue with the remainder of that operation. You shrink your power by one, and continue until your num is 0. Then you accumulate all numbers into a string.
Your code fixed:
def convert(num, base):
# get the highest power
power = 0
while num // (base**(power+1)) > 0:
power += 1
# divide, remember, subtract - until down to the lowest power
result = []
while num >= 0:
p = base**power
if p == 1:
result.append(num)
break
result.append(num // p)
num -= result[-1]*p
power -= 1
return ''.join(map(str,result))
to get an output of:
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 10 8 1000
9 11 9 1001
10 12 10 1010
11 13 11 1011
12 14 12 1100
13 15 13 1101
14 16 14 1110
15 17 15 1111
16 20 10 10000
Or you use built-ins:
def make(i):
for k in range(i+1):
print(f"{k:>10} {bin(k):>10} {hex(k):>10} {oct(k):>10}")
# or slice away the prefixes:
# print(f"{k:>10} {bin(k)[2:]:>10} {hex(k)[2:]:>10} {oct(k)[2:]:>10}")
make(17)
Results in:
0 0b0 0x0 0o0
1 0b1 0x1 0o1
2 0b10 0x2 0o2
3 0b11 0x3 0o3
4 0b100 0x4 0o4
5 0b101 0x5 0o5
6 0b110 0x6 0o6
7 0b111 0x7 0o7
8 0b1000 0x8 0o10
9 0b1001 0x9 0o11
10 0b1010 0xa 0o12
11 0b1011 0xb 0o13
12 0b1100 0xc 0o14
13 0b1101 0xd 0o15
14 0b1110 0xe 0o16
15 0b1111 0xf 0o17
16 0b10000 0x10 0o20
17 0b10001 0x11 0o21
The issue is you take only the mod of your number (num % base), that is to say the rightmost ("least significant") bit. What we want is not the least significant bit, but the entire decomposition.
NB: the problem here applies to all other bases too (decimal, hexadecimal...).
Indeed, if you run
n = 1000
print_formatted(n)
with your functions, you get that the decomposition of 1000 in different bases is:
1000 0 8 0
(all of them are wrong).
Here, I propose a recursive implementation:
def convert(integerToConvert, base = 2):
'''
When given a num and a base, will get the
conversion of that number in that base
'''
# The negative integer case is not taken into account
if (integerToConvert < 0):
print("ERROR: INTEGER < 0")
return;
# When the integer is 0, we know that we are done. There is no more bit
if (integerToConvert == 0):
print("WE ARE DONE")
return;
# get the current least significant coeff in the integerToEncode
currentLeastSignificant = integerToConvert % base;
print(currentLeastSignificant)
# remove the least significant coeff and start again
convert((integerToConvert - currentLeastSignificant) / base, base)
I ran a few quick tests:
convert(17, 2)
1
0.0
0.0
0.0
1.0
WE ARE DONE
convert(16, 2)
0
0.0
0.0
0.0
1.0
WE ARE DONE
convert(17, 16)
1
1.0
WE ARE DONE
NB1: I print numbers but you can store them in the data structure of your choice.
NB2: The most significant coefficient comes last in the printing (you can compare with your expected result)
NB3: all of these calculations are a bit expensive, so if speed matters to you, the best is actually to store all decompositions in arrays and access them (constant time).

python - adding each digit that is greater than 4

Given an integer. for each individual digit that is greater than 4, i need to add it to all the next digits that greater than 4.
For example: a = 4567; the result should be 0 + (5) + (5+6) + (5+6+7) = 34
So far in my code, I was able to get the sum for single digit only. If the integer is greater 10, it will only give the sum of the very first digit. Any idea why this happening?
def morethanfour(number):
num = 0
num = [int(d) for d in str(number)] #seperate into individual digit
total = 0
for i in range (len(num)):
if num[i] > 4:
total = sum(x for x in range(5, num[i]+1)) #adding the sum
return total
num = 9
print(morethanfour(num))
the result when num = 9 is 35 (5+6+7+8+9)
However, when num = 178, it gave me 0
Try this:
>>> def morethanfour(number):
return sum(sum(range(5,x+1)) for x in map(int,str(number)) if x>4)
>>> morethanfour(9)
35
>>> morethanfour(4567)
34
>>> morethanfour(178)
44
>>> sum(sum(num[j] for j in range(0, i+1) if num[j] > 4) for i in range(len(num)))
34

Categories

Resources