Python beginner - RGB values to HEX. How bad is my code? - python

I recently wrote a program that calculates the hex value when given rgb values. I was just wondering if my code is terrible (i did my best to write it from scratch without much help). I'm still a beginner and trying to learn.
Any help would be greatly appreciated (guidance about how i could do things better etc.).
Thank you
# sets the HEX letters for numbers above 9
hex_table = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8, '9':9,
'a':10, 'b':11, 'c':12, 'd':13, 'e':14, 'f':15}
# creates variable for the keys in dictionary
key_list = list(hex_table.keys())
# creates variable for values in dictionary
val_list = list(hex_table.values())
def test(r= int(input('red value: ')),g= int(input('green value: ')), b= int(input('blue value: '))):
# finds the index of the value
red_value = r // 16
green_value = g // 16
blue_value = b // 16
# Calcuate the remainder
red_float = float(r) / 16
red_remainder = red_float % 1
green_float = float(g) / 16
green_remainder = green_float % 1
blue_float = float(b) / 16
blue_remainder = blue_float % 1
# adds '#' in front of the result
print('#',end='')
#find the first two values in HEX code
if r >= 10:
print(key_list[val_list.index(red_value)],end='')
second_letter = (int(red_remainder * 16))
print(key_list[val_list.index(second_letter)],end='')
elif r <10:
print(red_value,end='')
print(int(red_remainder * 16),end='')
#find the next two values
if g >= 10:
print(key_list[val_list.index(green_value)],end='')
second_letter = (int(green_remainder * 16))
print(key_list[val_list.index(second_letter)],end='')
elif g <10:
print(green_value,end='')
print(int(green_remainder * 16),end='')
#find the last two values
if b >= 10:
print(key_list[val_list.index(blue_value)],end='')
second_letter = (int(blue_remainder * 16))
print(key_list[val_list.index(second_letter)],end='')
elif b <10:
print(blue_value,end='')
print(int(blue_remainder * 16),end='')
test()

You could reduce the amount of code by a lot by reducing the amount of times you repeat yourself, you are calculating the values for the digits for all three colours in the same function, therefore repeating yourself 3 times.
I've wrote my answer using JavaScript but I will explain what I am doing.
function getDigit(val) {
var alphabet = ["a", "b", "c", "d", "e", "f"]
if (val >= 10 && val < 16)
val = alphabet[val - 10];
return val.toString();
}
function helper(val) {
var first = Math.floor(val / 16)
var second = val % 16;
return getDigit(first) + getDigit(second)
}
function rgbToHex(red, green, blue) {
return helper(red) + helper(green) + helper(blue);
}
I have created two functions to help calculate the digits for each of the RGB values.
The helper() function calculates two numbers for the first and second digits. Lets use 24 and 172 as an example.
To find the first digit, you can divide the value by 16, and Floor the answer so it rounds down to a single digit.
24 / 16 = 1.5, Floor(1.5) = 1;
Therefore are first digit is 1.
And for the second digit we take the remainder of the value divided by 16.
24 % 16 = 8
So the full value for would be 18
Lets see 172 now.
172 / 16 = 10.75, Floor(10.75) = 10;
This will not work because the value should be "a" rather than "10", this is where the getDigit() function comes in, this function will take the value and check if it is between 10 and 15. We then take 10 from the value to find which letter we should use from the 'alphabet' array.
So for 10, we get 10 - 10 = 0; which means we will use the value at index 0 which gives us "a"
We can do the same for the second digit
172 % 16 = 12; Now the getDigit() function is called again.
12 - 10 = 2; so we take the item from index 2 in the array, which is "c"
so for 172 the value will be ac

Here's my slightly amended code:
hex_table = {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,
'A':10, 'B':11, 'C':12, 'D':13, 'E':14, 'F':15}
key_list = list(hex_table.keys())
val_list = list(hex_table.values())
def rgb(num_val):
num = num_val // 16
num_float = float(num_val) / 16
num_remainder = num_float % 1
if num_val >= 10:
print(key_list[val_list.index(num)], end='')
second_letter = (int(num_remainder * 16))
print(key_list[val_list.index(second_letter)],end='')
elif num_val <10:
print(num_val)
print(int(num_remainder * 16))
def rgb_hex(r,g,b):
print('#',end='')
return (f'{rgb(r)} {rgb(g)} {rgb(b)}')
rgb_hex(220,20,60)

This is an old question but for information, I developed a package with some utilities related to colors and colormaps and contains the rgb2hex function you were looking to convert triplet into hexa value (which can be found in many other packages, e.g. matplotlib). It's on pypi
pip install colormap
and then
>>> from colormap import rgb2hex
>>> rgb2hex(0, 128, 64)
'##008040'
Validity of the inputs is checked (values must be between 0 and 255).

Related

How can I convert an IntVector to an Int in z3py

I am using z3py and I have an IntVector of size 3. I need to parse each digit in the IntVector as one whole number. Meaning, if I have an IntVector which has constraints like this:
myIntVector = IntVector('iv', 3)
s = Solver()
s.add(iv[0] == 5)
s.add(iv[1] == 2)
s.add(iv[2] == 6)
….
I need to be able to operate on the number 526 as an Int sort in z3 because I need to both add constraints that apply to each individual member of the IntVector (digit) AND constraints which apply to the whole number, in this case 526. I cannot do something like:
s.add(iv[0] / iv == 55)
because those are 2 separate types. iv[0] is an Int while iv is an IntVector
Here is an example that uses the concept of IntVector as separate digits and as numbers formed by these digits.
It solves the traditional riddle to replace each of the letters of "SEND + MORE = MONEY" to a different digit.
from z3 import *
# trying to find different digits for each letter for SEND+MORE=MONEY
letters = 'SENDMOREMONEY'
iv = IntVector('iv', len(letters))
send = Int('send')
more = Int('more')
money = Int('money')
s = Solver()
# all letters to be replaced by digits 0..9
s.add([And(i >= 0, i <= 9) for i in iv])
# the first digit of a number can not be 0
s.add(And(iv[0] > 0, iv[4] > 0, iv[8] > 0))
# distinct letters need distinct digits
s.add(Distinct([i for k, i in enumerate(iv) if letters[k] not in letters[:k]]))
# "send" is the number formed by the first 4 digits, "more" the 4 next, "money" the last
s.add(send == Sum([10**(3-k)*i for k,i in enumerate(iv[:4])]))
s.add(more == Sum([10**(3-k)*i for k,i in enumerate(iv[4:8])]))
s.add(money == Sum([10**(4-k)*i for k,i in enumerate(iv[8:])]))
# the numbers for "send" and "more" sum together to "money"
s.add(send + more == money)
if s.check() == sat:
m = s.model()
# list each digit of iv
print([m[i].as_long() for i in iv])
# show the sum as "send" + "more" = "money"
print("{} + {} = {}".format(m[send].as_long(), m[more].as_long(), m[money].as_long()))

Converting Decimal to Binary, how to get 8-bit binary?

So I need to convert decimal to binary but all of my returns need to be 8 bits long.
I've already figured out the conversion itself but I need to figure out how to add zero's to the beginning if it's less than 8 bits.
old_number = int(input("Enter whole number here: "))
number = old_number
binary_translation = 0
while number > -1:
if number > 128 or number == 128:
binary_translation = binary_translation + 10000000
number = number - 128
elif number > 64 or number == 64:
binary_translation = binary_translation + 1000000
number = number - 64
elif number > 32 or number == 32:
binary_translation = binary_translation + 100000
number = number - 32
etc all the way zero...
print("The number", old_number, "is", binary_translation, "in binary.")
Result I want if number = 39 - 00100111
Result I get if number = 39 - 100111
TLDR: Use it: '{:0>8}'.format(str(bin(a))[2:])
You make unnecessary binary transformations. Python have built-in function bin() that can do it for you:
>>> number = 39
>>> bin(number)
'0b100111'
You can crop it:
>>> str(bin(number))[2:]
'100111'
And add forward zeros:
>>> '{:0>8}'.format(str(bin(a))[2:])
'00100111'
Here is the final one-liner for you: '{:0>8}'.format(str(bin(a))[2:])
If you want to learn more about this: '{:0>8}' magic, you can read this article.
I wrote a JS function
function getBinary(input) {
if (input > 255) {
return "fu";
}
//reverse func
function reverse(s){
return s.split("").reverse().join("");
}
//variable definition
var rest;
var temp = 1;
var str="";
var zeroeslength;
//magic
while (temp > 0) {
temp = Math.floor(input / 2);
rest = input - (temp * 2);
input = temp;
str = str + rest;
}
str = reverse(str);
//check length
zeroeslength = 8 - str.length;
//if length < 8 : add zeroes
while (zeroeslength > 0) {
str = 0 + str;
zeroeslength--;
}
//output
return str;
}

python - print squares of numbers which are palindromes : improve efficiency

I have an assignment to do. The problem is something like this. You give a number, say x. The program calculates the square of the numbers starting from 1 and prints it only if it's a palindrome. The program continues to print such numbers till it reaches the number x provided by you.
I have solved the problem. It works fine for uptil x = 10000000. Works fine as in executes in a reasonable amount of time. I want to improve upon the efficiency of my code. I am open to changing the entire code, if required. My aim is to make a program that could execute 10^20 within around 5 mins.
limit = int(input("Enter a number"))
def palindrome(limit):
count = 1
base = 1
while count < limit:
base = base * base #square the number
base = list(str(base)) #convert the number into a list of strings
rbase = base[:] #make a copy of the number
rbase.reverse() #reverse this copy
if len(base) > 1:
i = 0
flag = 1
while i < len(base) and flag == 1:
if base[i] == rbase[i]: #compare the values at the indices
flag = 1
else:
flag = 0
i += 1
if flag == 1:
print(''.join(base)) #print if values match
base = ''.join(base)
base = int(base)
base = count + 1
count = count + 1
palindrome(limit)
He're my version:
import sys
def palindrome(limit):
for i in range(limit):
istring = str(i*i)
if istring == istring[::-1]:
print(istring,end=" ")
print()
palindrome(int(sys.argv[1]))
Timings for your version on my machine:
pu#pumbair: ~/Projects/Stackexchange time python3 palin1.py 100000
121 484 676 10201 12321 14641 40804 44944 69696 94249 698896 1002001 1234321
4008004 5221225 6948496 100020001 102030201 104060401 121242121 123454321 125686521
400080004 404090404 522808225 617323716 942060249
real 0m0.457s
user 0m0.437s
sys 0m0.012s
and for mine:
pu#pumbair: ~/Projects/Stackexchange time python3 palin2.py 100000
0 1 4 9
121 484 676 10201 12321 14641 40804 44944 69696 94249 698896 1002001 1234321
4008004 5221225 6948496 100020001 102030201 104060401 121242121 123454321 125686521
400080004 404090404 522808225 617323716 942060249
real 0m0.122s
user 0m0.104s
sys 0m0.010s
BTW, my version gives more results (0, 1, 4, 9).
Surely something like this will perform better (avoiding the unnecessary extra list operations) and is more readable:
def palindrome(limit):
base = 1
while base < limit:
squared = str(base * base)
reversed = squared[::-1]
if squared == reversed:
print(squared)
base += 1
limit = int(input("Enter a number: "))
palindrome(limit)
I think we can do it a little bit easier.
def palindrome(limit):
count = 1
while count < limit:
base = count * count # square the number
base = str(base) # convert the number into a string
rbase = base[::-1] # make a reverse of the string
if base == rbase:
print(base) #print if values match
count += 1
limit = int(input("Enter a number: "))
palindrome(limit)
String into number and number into string conversions were unnecessary. Strings can be compared, this is why you shouldn't make a loop.
You can keep a list of square palindromes upto a certain limit(say L) in memory.If the Input number x is less than sqrt(L) ,you can simply iterate over the list of palindromes and print them.This way you wont have to iterate over every number and check if its square is palindrome .
You can find a list of square palindromes here : http://www.fengyuan.com/palindrome.html
OK, here's my program. It caches valid suffixes for squares (i.e. the values of n^2 mod 10^k for a fixed k), and then searches for squares which have both that suffix and start with the suffix reversed. This program is very fast: in 24 seconds, it lists all the palindromic squares up to 10^24.
from collections import defaultdict
# algorithm will print palindromic squares x**2 up to x = 10**n.
# efficiency is O(max(10**k, n*10**(n-k)))
n = 16
k = 6
cache = defaultdict(list)
print 0, 0 # special case
# Calculate everything up to 10**k; these will be the prefix/suffix pairs we use later
tail = 10**k
for i in xrange(tail):
if i % 10 == 0: # can't end with 0 and still be a palindrome
continue
sq = i*i
s = str(sq)
if s == s[::-1]:
print i, s
prefix = int(str(sq % tail).zfill(k)[::-1])
cache[prefix].append(i)
prefixes = sorted(cache)
# Loop through the rest, but only consider matching prefix/suffix pairs
for l in xrange(k*2+1, n*2+1):
for p in prefixes:
low = (p * 10**(l-k))**.5
high = ((p+1) * 10**(l-k))**.5
low = int(low / tail) * tail
high = (int(high / tail) + 1) * tail
for n in xrange(low, high, tail):
for suf in cache[p]:
x = n + suf
s = str(x*x)
if s == s[::-1]:
print x, s
Sample output:
0 0
1 1
2 4
3 9
11 121
22 484
26 676
101 10201
111 12321
121 14641
202 40804
212 44944
<snip>
111010010111 12323222344844322232321
111100001111 12343210246864201234321
111283619361 12384043938083934048321
112247658961 12599536942224963599521
128817084669 16593841302620314839561
200000000002 40000000000800000000004

Benford's law program

I have to write a program that proves Benford's Law for two Data lists. I think I have the code down for the most part but I think there are small errors that I am missing. I am sorry if this is not how the site is supposed to be used but I really need help. Here is my code.
def getData(fileName):
data = []
f = open(fileName,'r')
for line in f:
data.append(line)
f.close()
return data
def getLeadDigitCounts(data):
counts = [0,0,0,0,0,0,0,0,0]
for i in data:
pop = i[1]
digits = pop[0]
int(digits)
counts[digits-1] += 1
return counts
def showResults(counts):
percentage = 0
Sum = 0
num = 0
Total = 0
for i in counts:
Total += i
print"number of data points:",Sum
print
print"digit number percentage"
for i in counts:
Sum += i
percentage = counts[i]/float(Sum)
num = counts[i]
print"5%d 6%d %f"%(i,num,percentage)
def showLeadingDigits(digit,data):
print"Showing data with a leading",digit
for i in data:
if digit == i[i][1]:
print i
def processFile(name):
data = getData(name)
counts = getLeadDigitCounts(data)
showResults(counts)
digit = input('Enter leading digit: ')
showLeadingDigits(digit, data)
def main():
processFile('TexasCountyPop2010.txt')
processFile('MilesofTexasRoad.txt')
main()
Again sorry if this is not how I am supposed to use this site. Also, I can only use programming techniques that the professor has showed us so if you could just give me advice to clean up the code as it is I would really appreciate it.
Also, here are a few lines from my data.
Anderson County 58458
Andrews County 14786
Angelina County 86771
Aransas County 23158
Archer County 9054
Armstrong County 1901
Your error is coming from this line:
int(digits)
This doesn't actually do anything to digits. If you want to convert digits to an integer, you have to re-set the variable:
digits = int(digits)
Also, to properly parse your data, I would do something like this:
for line in data:
place, digits = line.rsplit(None, 1)
digits = int(digits)
counts[digits - 1] += 1
Lets walk though one cycle of your code and I think you'll see what the problem is. I'll be using this file here for data
An, 10, 22
In, 33, 44
Out, 3, 99
Now getData returns:
["An, 10, 22",
"In, 33, 44",
"Out, 3, 99"]
Now take a look the first pass though the loop:
for i in data:
# i = "An, 10, 22"
pop = i[1]
# pop = 'n', the second character of i
digits = pop[0]
# digits = 'n', the first character of pop
int(digits)
# Error here, but you probably wanted digits = int(digits)
counts[digits-1] += 1
Depending on how your data is structured, you need to figure out the logic to extract the digits you expect to get from your file. This logic might do better in the getData funciton, but it mostly depends on the specifics of your data.
Just to share here a different (and maybe more step-by-step) code. It's RUBY.
The thing is, Benford's Law doesn't apply when you have a specific range of random data to extract from. The maximum number of the data set that you are extracting random information from must be undetermined, or infinite.
In other words, say, you used a computer number generator that had a 'set' or specific range from which to extract the numbers, eg. 1-100. You would undoubtedly end up with a random dataset of numbers, yes, but the number 1 would appear as a first digit as often as the number 9 or any other number.
**The interesting** part, actually, happens when you let a computer (or nature) decide randomly, and on each instance, how large you want the random number to potentially be. Then you get a nice, bi-dimensional random dataset, that perfectly attains to Benford's Law. I have generated this RUBY code for you, which will neatly prove that, to our fascination as Mathematicians, Benford's Law works each and every single time!
Take a look at this bit of code I've put together for you!
It's a bit WET, but I'm sure it'll explain.
<-- RUBY CODE BELOW -->
dataset = []
999.times do
random = rand(999)
dataset << rand(random)
end
startwith1 = []
startwith2 = []
startwith3 = []
startwith4 = []
startwith5 = []
startwith6 = []
startwith7 = []
startwith8 = []
startwith9 = []
dataset.each do |element|
case element.to_s.split('')[0].to_i
when 1 then startwith1 << element
when 2 then startwith2 << element
when 3 then startwith3 << element
when 4 then startwith4 << element
when 5 then startwith5 << element
when 6 then startwith6 << element
when 7 then startwith7 << element
when 8 then startwith8 << element
when 9 then startwith9 << element
end
end
a = startwith1.length
b = startwith2.length
c = startwith3.length
d = startwith4.length
e = startwith5.length
f = startwith6.length
g = startwith7.length
h = startwith8.length
i = startwith9.length
sum = a + b + c + d + e + f + g + h + i
p "#{a} times first digit = 1; equating #{(a * 100) / sum}%"
p "#{b} times first digit = 2; equating #{(b * 100) / sum}%"
p "#{c} times first digit = 3; equating #{(c * 100) / sum}%"
p "#{d} times first digit = 4; equating #{(d * 100) / sum}%"
p "#{e} times first digit = 5; equating #{(e * 100) / sum}%"
p "#{f} times first digit = 6; equating #{(f * 100) / sum}%"
p "#{g} times first digit = 7; equating #{(g * 100) / sum}%"
p "#{h} times first digit = 8; equating #{(h * 100) / sum}%"
p "#{i} times first digit = 9; equating #{(i * 100) / sum}%"

leading number groups between two numbers

(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.

Categories

Resources