Two trigonometry-based turtle codes not giving similar output - python-3.x

This is my first time posting a question.
I'm having trouble creating a code involving cosine, and I am not recieving the desired outcome. What is even more confusing is the fact that the two codes should be creating similar images (Explained later). Any ideas?
In the code below, these variables represent:
Y is a counter, making sure that the code only runs until the specified amount of radi is produced.
W is the colour randomly generated.
Z is the angle turn from 0 degrees. (The turtle's angle resets due to turtle.home).
Adjacent is the smallest length from centre to a line.
Radi is the amount of lines protruding from the centre.
def Triangle(Radi, Adjacent):
y = 0
if (Radi) % 1 == 0:
while (Radi) > y:
y = y + 1
w = randhex()
z = 360/(Radi)*y
turtle.left(z+30)
turtle.color(w)
if z > 300:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 300))/180))
elif z > 240:
turtle.forward(Adjacent/math.cos(math.pi*(z - 240)/180))
elif z > 180:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 180))/180))
elif z > 120:
turtle.forward(Adjacent/math.cos(math.pi*(z - 120)/180))
elif z > 60:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 60))/180))
else:
turtle.forward(Adjacent/math.cos(math.pi*z/180))
turtle.home()
Above is my first code which appears to work, giving these results when Triangle(100,180) is entered (Please note that randhex() is a custom function that generates random colours).
Triangle(100,180) results.
My apologies if my variable naming creativity is annoying.
In this code, counter represents 'y' and angle represents 'z' from the previous code
Here is my second code:
def Polygon(Radi, Adjacent, Sides):
counter = 0
if Sides % 1 != 0 or Sides == 2 or Sides <= 0:
print ("INVALID")
elif Sides == 1:
while Radi > counter:
counter = counter + 1
colour = randhex()
turn = 360/Radi*counter
turtle.left(turn)
turtle.color(colour)
turtle.forward(Adjacent)
turtle.home()
else:
while Radi > counter:
counter = counter + 1
colour = randhex()
turn = 360/Radi*counter
turtle.left(turn)
turtle.color(colour)
segment = str(counter/Radi*Sides*2)
position = segment.index('.')
test = int(segment[:position])
if test % 2 == 1:
length = Adjacent/math.cos(math.pi*(turn - (360 - 360/Sides*((test+1)/2)))/180)
turtle.forward(length)
else:
length = Adjacent/math.cos(math.pi*(180/Sides - (turn - (360 - 180/Sides*(test+1))))/180)
turtle.forward(length)
turtle.home()
Above is my second code, being the one I'm struggling with. Once again, apologies for my variable names being annoying and some of the maths not simplified. I find it easier to see how my ideas make sense when I leave them as they are. Below are my results for my second code after entering Polygon(180,100,3).
Polygon(180,100,3) results.
As you can see, it didn't go quite how I was planning.
I should also note that I tried substituting the numbers into the codes where one of the codes were giving a different line length. Sometimes they even went in an opposite direction (because the number came out negative). I did this on the Google calculator, but it seemed that both codes would give the same answer, but they corresponded to what the second code was outputing, not the first.
If you want me to explain anything leave a comment.
But if it turns out that my code is wrong (Which I believe), could you please point me to what I need to do instead.
I'd appreciate the help.

Your code is too complicated to debug. The unhelpful variable names, the lack of comments and excessively long equations make it hard to read.
If we consider the equation suggested in this answer to Is there an equation to describe regular polygons? then your original triangle code simplifies to:
import math
import turtle
def randhex():
""" substitute random color generator here """
return 'red'
def triangle(radii, adjacent):
if radii % 1 != 0: # only whole numbers (int or float) allowed
return
counter = 1
while counter <= radii:
angle = counter * (2 * math.pi / radii)
turtle.setheading(angle)
colour = randhex()
turtle.color(colour)
radius = adjacent / math.cos(angle % (math.pi / 1.5) - math.pi / 3)
turtle.forward(radius)
turtle.backward(radius)
counter += 1
turtle.radians() # avoid individual conversions, switch to radians
triangle(100, 180)
turtle.exitonclick()
And the general polygon solution can be achieved with just a few changes:
import math
import turtle
def randhex():
""" substitute random color generator here """
return 'red'
def polygon(radii, adjacent, sides):
if radii % 1 != 0: # only whole numbers (int or float) allowed
return
if sides % 1 != 0 or sides == 2 or sides <= 0:
return
counter = 1
while counter <= radii:
angle = counter * (2 * math.pi / radii)
turtle.setheading(angle)
colour = randhex()
turtle.color(colour)
if sides == 1: # special case, a circle
radius = adjacent
else:
radius = adjacent / math.cos(angle % (math.pi / (sides / 2)) - math.pi / sides)
turtle.forward(radius)
turtle.backward(radius)
counter += 1
turtle.radians() # avoid individual conversions, switch to radians
polygon(100, 180, 3)
turtle.exitonclick()
With polygon(100, 90, 5) looking like:

Related

I'm Looking for ways to make this lexicographical code faster

I've been working on code to calculate the distance between 33 3D points and calculate the shortest route is between them. The initial code took in all 33 points and paired them consecutively and calculated the distances between the pairs using math.sqrt and sum them all up to get a final distance.
My problem is that with the sheer number of permutations of a list with 33 points (33 factorial!) the code is going to need to be at its absolute best to find the answer within a human lifetime (assuming I can use as many CPUs as I can get my hands on to increase the sheer computational power).
I've designed a simple web server to hand out an integer and convert it to a list and have the code perform a set number of lexicographical permutations from that point and send back the resulting shortest distance of that block. This part is fine but I have concerns over the code that does the distance calculations
I've put together a test version of my code so I could change things and see if it made the execution time faster or slower. This code starts at the beginning of the permutation list (0 to 32) in order and performs 50 million lexicographical iterations on it, checking the distance of the points at every iteration. the code is detailed below.
import json
import datetime
import math
def next_lexicographic_permutation(x):
i = len(x) - 2
while i >= 0:
if x[i] < x[i+1]:
break
else:
i -= 1
if i < 0:
return False
j = len(x) - 1
while j > i:
if x[j] > x[i]:
break
else:
j-= 1
x[i], x[j] = x[j], x[i]
reverse(x, i + 1)
return x
def reverse(arr, i):
if i > len(arr) - 1:
return
j = len(arr) - 1
while i < j:
arr[i], arr[j] = arr[j], arr[i]
i += 1
j -= 1
# ip for initial permutation
ip = [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]
lookup = '{"0":{"name":"van Maanen\'s Star","x":-6.3125,"y":-11.6875,"z":-4.125},\
"1":{"name":"Wolf 124","x":-7.25,"y":-27.1562,"z":-19.0938},\
"2":{"name":"Midgcut","x":-14.625,"y":10.3438,"z":13.1562},\
"3":{"name":"PSPF-LF 2","x":-4.40625,"y":-17.1562,"z":-15.3438},\
"4":{"name":"Wolf 629","x":-4.0625,"y":7.6875,"z":20.0938},\
"5":{"name":"LHS 3531","x":1.4375,"y":-11.1875,"z":16.7812},\
"6":{"name":"Stein 2051","x":-9.46875,"y":2.4375,"z":-15.375},\
"7":{"name":"Wolf 25","x":-11.0625,"y":-20.4688,"z":-7.125},\
"8":{"name":"Wolf 1481","x":5.1875,"y":13.375,"z":13.5625},\
"9":{"name":"Wolf 562","x":1.46875,"y":12.8438,"z":15.5625},\
"10":{"name":"LP 532-81","x":-1.5625,"y":-27.375,"z":-32.3125},\
"11":{"name":"LP 525-39","x":-19.7188,"y":-31.125,"z":-9.09375},\
"12":{"name":"LP 804-27","x":3.3125,"y":17.8438,"z":43.2812},\
"13":{"name":"Ross 671","x":-17.5312,"y":-13.8438,"z":0.625},\
"14":{"name":"LHS 340","x":20.4688,"y":8.25,"z":12.5},\
"15":{"name":"Haghole","x":-5.875,"y":0.90625,"z":23.8438},\
"16":{"name":"Trepin","x":26.375,"y":10.5625,"z":9.78125},\
"17":{"name":"Kokary","x":3.5,"y":-10.3125,"z":-11.4375},\
"18":{"name":"Akkadia","x":-1.75,"y":-33.9062,"z":-32.9688},\
"19":{"name":"Hill Pa Hsi","x":29.4688,"y":-1.6875,"z":25.375},\
"20":{"name":"Luyten 145-141","x":13.4375,"y":-0.8125,"z":6.65625},\
"21":{"name":"WISE 0855-0714","x":6.53125,"y":-2.15625,"z":2.03125},\
"22":{"name":"Alpha Centauri","x":3.03125,"y":-0.09375,"z":3.15625},\
"23":{"name":"LHS 450","x":-12.4062,"y":7.8125,"z":-1.875},\
"24":{"name":"LP 245-10","x":-18.9688,"y":-13.875,"z":-24.2812},\
"25":{"name":"Epsilon Indi","x":3.125,"y":-8.875,"z":7.125},\
"26":{"name":"Barnard\'s Star","x":-3.03125,"y":1.375,"z":4.9375},\
"27":{"name":"Epsilon Eridani","x":1.9375,"y":-7.75,"z":-6.84375},\
"28":{"name":"Narenses","x":-1.15625,"y":-11.0312,"z":21.875},\
"29":{"name":"Wolf 359","x":3.875,"y":6.46875,"z":-1.90625},\
"30":{"name":"LAWD 26","x":20.9062,"y":-7.5,"z":3.75},\
"31":{"name":"Avik","x":13.9688,"y":-4.59375,"z":-6.0},\
"32":{"name":"George Pantazis","x":-12.0938,"y":-16.0,"z":-14.2188}}'
lookup = json.loads(lookup)
lowest_total = 9999
# create 2D array for the distances and called it b to keep code looking clean.
b = [[0 for i in range(33)] for j in range(33)]
for x in range(33):
for y in range(33):
if x == y:
continue
else:
b[x][y] = math.sqrt(((lookup[str(x)]["x"] - lookup[str(y)]['x']) ** 2) + ((lookup[str(x)]['y'] - lookup[str(y)]['y']) ** 2) + ((lookup[str(x)]['z'] - lookup[str(y)]['z']) ** 2))
# begin timer
start_date = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
start = datetime.datetime.now()
print("[{}] Start".format(start_date))
# main iteration loop
for x in range(50_000_000):
distance = b[ip[0]][ip[1]] + b[ip[1]][ip[2]] + b[ip[2]][ip[3]] +\
b[ip[3]][ip[4]] + b[ip[4]][ip[5]] + b[ip[5]][ip[6]] +\
b[ip[6]][ip[7]] + b[ip[7]][ip[8]] + b[ip[8]][ip[9]] +\
b[ip[9]][ip[10]] + b[ip[10]][ip[11]] + b[ip[11]][ip[12]] +\
b[ip[12]][ip[13]] + b[ip[13]][ip[14]] + b[ip[14]][ip[15]] +\
b[ip[15]][ip[16]] + b[ip[16]][ip[17]] + b[ip[17]][ip[18]] +\
b[ip[18]][ip[19]] + b[ip[19]][ip[20]] + b[ip[20]][ip[21]] +\
b[ip[21]][ip[22]] + b[ip[22]][ip[23]] + b[ip[23]][ip[24]] +\
b[ip[24]][ip[25]] + b[ip[25]][ip[26]] + b[ip[26]][ip[27]] +\
b[ip[27]][ip[28]] + b[ip[28]][ip[29]] + b[ip[29]][ip[30]] +\
b[ip[30]][ip[31]] + b[ip[31]][ip[32]]
if distance < lowest_total:
lowest_total = distance
ip = next_lexicographic_permutation(ip)
# end timer
finish_date = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
finish = datetime.datetime.now()
print("[{}] Finish".format(finish_date))
diff = finish - start
print("Time taken => {}".format(diff))
print("Lowest distance => {}".format(lowest_total))
This is the result of a lot of work to make things faster. I was initially using string look-ups to find the distance to be calculated with a dict having keys like "1-2", but very quickly found out that it was very slow, I then moved onto hashed versions of the "1-2" key and the speed increased but the fastest way I have found so far is using a 2D array and looking up the values from there.
I have also found that manually constructing the distance calculation saved time over having a for x in ranges(32): loop adding the distances up and incrementing a variable to get the total.
Another great speed up was using pypy3 instead of python3 to execute it.
This usually takes 11 seconds to complete using pypy3
running 50 million of the distance calculation on its own takes 5.2 seconds
running 50 million of the next_lexicographic_permutation function on its own takes 6 seconds
I can't think of any way to make this faster and I believe there may be optimizations to be made in the next_lexicographic_permutation function. From what I've read about this the main bottleneck seems to be the switching of positions in the array:
x[i], x[j] = x[j], x[i]
Edit : added clarification of lifetime to represent human lifetime
The brute-force approach of calculating all the distances is going to be slower than a partitioning approach. Here is a similar question for the 3D case.

Minimum replacements in a string to make all 'X' chars to the left of all 'Y chars

Hey everyone had this problem in an interview and can't seem to figure out the best way to do it. Any help would be much appreciated.
Problem:
You are given a string S. Each character of the string is either 'W' or 'R'.
W represents a white-colored flower.
R represents a red-colored flower
A string is considered beautiful if all the white-colored flowers are on the left of all the red-colored flowers. You can replace any white-colored flower with a red-colored flower and vice versa.
Your task is to find the minimum number of replacements that must be made to make string S beautiful.
sample cases:
1.) input: "RWRW"
output: 2 ("WWRR" or "WWWW" or “RRRR”)
2.) input: "RRRR"
output: 0 ("RRRR")
3.) input: "RWWWRR"
output: 1 ("WWWWRR")
This can be solved in a single pass through the array keeping O(1) state.
We do this by finding the best place to split the array so that in the solution all the flowers to the left are white and all the flowers to the right are red. That's the same as finding i from 0 to N that minimizes the number of red flowers to the left of i plus the number of white flowers to the right of i, because that's the numbers of flowers we'd have to change to split the array at i.
Suppose the length of the array is N, there's R total red flowers (so N-R red flowers), and let r[i] be the number of red flowers to the left of i, r'[i] the number of red flowers to the right of i, and the same for white flowers with w[i] and w'[i].
We want to find i such that r[i] + w'[i] is minimized (the number of red flowers to the left of i plus the number of white flowers to the right of i -- these are the ones we'd have to change).
But w'[i] + w[i] = (N-R), and r[i] + w[i] = i, so r[i] + w'[i] = r[i] + (N-i)-(R-r[i]) = 2r[i] - i + (N - R).
Thus we need to find i that minimizes 2r[i] - i + (N - R). Since N-R is constant, that's the same as finding i such that 2r[i] - i is minimized. By the time we've processed the whole array, we have the value for R, and can construct the return value.
Here's some python code that uses this method, along with some test cases.
def min_changes(A):
best, besti = 1e12, -1
r = 0
for i in range(len(A)+1):
v = 2 * r - i
if v < best:
besti, best = i, v
if i < len(A):
r += A[i] == 'R'
return best + len(A) - r, 'W' * besti + 'R' * (len(A) - besti)
tests = [("RWRW", 2, "RRRR"), ("RRRR", 0, "RRRR"), ("RWWWRR", 1, "WWWWRR")]
for s, n, r in tests:
gotn, gotr = min_changes(s)
if gotn != n or gotr != r:
print("FAILED",)
print(s, gotn, gotr)
This looks like a dynamic programming problem. Let
dp[i][0] = minimum number of changes if the i-th flower is white
dp[i][1] = minimum number of changes if the i-th flower is white
Base state:
dp[0][0] = int(arr[0] != “W”)
dp[0][1] = int(arr[0] != “R”)
Now, to calculate the values for dp[i], we look at the cases:
If arr[i] == “R”
If arr[i-1] == “R”, then arr[i] has to be “R”.
If arr[i-1] == “W”, then arr[i] can be “R” or “W”.
arr[i][1] = min(arr[i-1][0], arr[i-1][1])
arr[i][0] = arr[i-1][0] + 1
If arr[i] == “W”
If arr[i-1] == “R”, then arr[i] has to be “R” (so we’ll have to modify current flower).
If arr[i-1] == “W”, then arr[i] can be “R” (which needs modification) or “W” (no modification needed).
arr[i][0] = arr[i-1][0]
arr[i][1] = min(arr[i-1][0], arr[i-1][1]) + 1
In the end, your answer becomes min(dp[n-1][0], dp[n-1][1])
Simple Python implementation:
s = "RWWWRR"
n = len(s)
dp = [[0,0] for i in range(n)]
dp[0][0] = int(s[0] != "W")
dp[0][1] = int(s[0] != "R")
for i in range(1,n):
if s[i] == "R":
dp[i][1] = min(dp[i-1])
dp[i][0] = dp[i-1][0] + 1
else:
dp[i][1] = min(dp[i-1]) + 1
dp[i][0] = dp[i-1][0]
print(min(dp[n-1]))

Am I doing this while loop correctly? [duplicate]

This question already has answers here:
How do I plot this logarithm without a "while True" loop?
(2 answers)
Closed 3 years ago.
I am trying to plot the logarithm of twelve tone equal temperament on a scale of hertz.
Is this while loop that breaks in the middle the best way to iterate all of the audible notes in the scale? Could I do the same thing more accurately, or with less code?
I do not want to use a for loop because then the range would be defined arbitrarily, not by the audible range.
When I try to use "note > highest or note < lowest" as the condition for the while loop, it doesn't work. I'm assuming that's because of the scope of where "note" is defined.
highest = 20000
lowest = 20
key = 440
TET = 12
equal_temper = [key]
i = 1
while True:
note = key * (2**(1/TET))**i
if note > highest or note < lowest:
break
equal_temper.append(note)
i += 1
i = 1
while True:
note = key * (2**(1/TET))**-i
if note > highest or note < lowest:
break
equal_temper.append(note)
i += 1
equal_tempered = sorted(equal_temper)
for i in range(len(equal_temper)):
print(equal_tempered[i])
The code returns a list of pitches (in hertz) that are very close to other tables I have looked at, but the higher numbers are further off. Setting a while loop to loop indefinitely seems to work, but I suspect there may be a more elegant way to write the loop.
As it turns out, you actually know the number of iterations! At least you can calculate it by doing some simple math. Then you can use a list comprehension to build your list:
import math
min_I = math.ceil(TET*math.log2(lowest/key))
max_I = math.floor(TET*math.log2(highest/key))
equal_tempered = [key * 2 ** (i / TET) for i in range(min_I, max_I + 1)]
You can use the piano key formula:
freq_n = freq_ref * sqrt(2, 12) ** (n − a)
The reference note is A4, 440 Hz and 49th key on the piano:
def piano_freq(key_no: int) -> float:
ref_tone = 440
ref_no = 49
freq_ratio = 2 ** (1/12)
return ref_tone * freq_ratio ** (key_no - ref_no)
Then you can do things like:
print(piano_freq(40)) # C4 = 261.6255653005985
print([piano_freq(no) for no in range(49, 49+12)]) # A4 .. G#5
Based on: https://en.wikipedia.org/wiki/Piano_key_frequencies

Project Euler #23 Optimization [Python 3.6]

I'm having trouble getting my code to run quickly for Project Euler Problem 23. The problem is pasted below:
A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
And my code:
import math
import bisect
numbers = list(range(1, 20162))
tot = set()
numberabundance = []
abundant = []
for n in numbers:
m = 2
divisorsum = 1
while m <= math.sqrt(n):
if n % m == 0:
divisorsum += m + (n / m)
m += 1
if math.sqrt(n) % 1 == 0:
divisorsum -= math.sqrt(n)
if divisorsum > n:
numberabundance.append(1)
else:
numberabundance.append(0)
temp = 1
# print(numberabundance)
for each in numberabundance:
if each == 1:
abundant.append(temp)
temp += 1
abundant_set = set(abundant)
print(abundant_set)
for i in range(12, 20162):
for k in abundant:
if i - k in abundant_set:
tot.add(i)
break
elif i - k < i / 2:
break
print(sum(numbers.difference(tot)))
I know the issue lies in the for loop at the bottom but I'm not quire sure how to fix it. I've tried modeling it after some of the other answers I've seen here but none of them seem to work. Any suggestions? Thanks.
Your upper bound is incorrect - the question states all integers greater than 28123 can be written ..., not 20162
After changing the bound, generation of abundant is correct, although you could do this generation in a single pass by directly adding to a set abundant, instead of creating the bitmask array numberabundance.
The final loop is also incorrect - as per the question, you must
Find the sum of all the positive integers
whereas your code
for i in range(12, 20162):
will skip numbers below 12 and also doesn't include the correct upper bound.
I'm a bit puzzled about your choice of
elif i - k < i / 2:
Since the abundants are already sorted, I would just check if the inner loop had passed the midpoint of the outer loop:
if k > i / 2:
Also, since we just need the sum of these numbers, I would just keep a running total, instead of having to do a final sum on a collection.
So here's the result after making the above changes:
import math
import bisect
numbers = list(range(1, 28123))
abundant = set()
for n in numbers:
m = 2
divisorsum = 1
while m <= math.sqrt(n):
if n % m == 0:
divisorsum += m + (n / m)
m += 1
if math.sqrt(n) % 1 == 0:
divisorsum -= math.sqrt(n)
if divisorsum > n:
abundant.add(n)
#print(sorted(abundant))
nonabundantsum = 0
for i in numbers:
issumoftwoabundants = False
for k in abundant:
if k > i / 2:
break
if i - k in abundant:
issumoftwoabundants = True
break
if not issumoftwoabundants:
nonabundantsum += i
print(nonabundantsum)
Example here

Changing radius/degree to x,y coordinate for output of a value at a point

I'm working on my final project for class, which is a program that displays the HSV values for a color and then allows the user to click on the color to display it and then choose a lightness value for it. There is a lot of code that I don't want to post here, so I've made a few pastebin posts and linked them here.
My friend/classmate made a circular color wheel, and I am trying to adapt his color selection method into a rectangular color spectrum so we can have both to display for the professor and write about the pros/cons, etc. of each.
My color displays correctly, but I am having trouble getting the color selection to work, and I have a sneaking suspicion that it has to do with this part:
def color_from_point(x, y, l):
degree = abs(degrees(atan(y / x)))
c = abs(x / cos(radians(degree)))
if x < 0 and y >= 0:
degree = (90 - degree) + 90
elif x < 0 and y < 0:
degree = degree + 180
elif x >= 0 and y <= 0:
degree = (90 - degree) + 270
if c > 255:
color = "White"
else:
color = colors(c / 255, degree, (l / 100))
return color
I am a complete beginner coder, and I'm not sure what to do to change this from using radius and degrees to using just the x,y coordinate to give me the color.
Here is the link to the pastebin post of the circular color picker of a friend/classmate:
http://pastebin.com/gAEg6rA1
Link to my WIP rectangular version:
http://pastebin.com/MJcqvvJw
Thanks in advance for any and all help!!

Resources