I have been trying to plot the twelve tone scale, from the lowest to the highest audible pitch, in hertz.
I found a way to do this using a while loop that is always true and then use break to exit the loop once the pitch is either lower or higher than the audible human range.
I know that this kind of loop is bad practice, but--as a novice programmer--I can't think of how to do this the proper way. How can I achieve the same result without using "while True"?
I have tried putting the condition "note > highest or note < lowest" where "True" is currently in the while loop, but then "note" is only defined within the loop so I get a "NameError: name 'note' is not defined" error.
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 output to the terminal seems to be correct. It may round down or up differently than other tables I have seen. I just want to know how to do this without using "while True."
I notice you're making your compiler re-make the same calculations over and over again while they only need to be done once! (And they can be done by you).
You have a set equation for your note value: note = key * ( 2 ** (1/TET) ) ** i. This can easily be inverted to solve for i as a function of note: i = TET * log_2(note / key). With this, and knowing your upper and lower bounds for note, we can determine the bounds for i.
Plugging in your upper limit, 20000 gives i=66.07. Anything above this will result in a note greater than your upper limit, so we want to take the floor of this value. i_upper = 66.
Plugging in your lower limit, 20, gives i=-53.51. Anything lower than this will give a note lower than your lower limit, so we want to take the ceiling of this value. i_lower = -53
Now all we have to do is loop from i_lower to i_upper and fill your array with the note values.
This method avoids using while loops, is easily generalized if you change any of the parameters, and just through construction, returns a sorted array so you don't have to sort it afterwards.
import math
highest = 20000
lowest = 20
key = 440
TET = 12
def note_value(key, TET, i):
""" Returns the note value for a given key, TET, and i"""
return key * (2**(1/TET))**i
def i_value(key, TET, N):
""" The inverse of note_value. Re-arranged to solve for i."""
return TET * math.log(N/key) / math.log(2)
notes_above = math.floor(i_value(key, TET, highest))
print(f"Number of notes above = {notes_above}")
notes_below = - math.ceil(i_value(key, TET, lowest))
print(f"Number of notes below = {notes_below}")
print(f"Total number of notes = {notes_below+notes_above+1}")
equal_temper = [ ]
for i in range(-notes_below, notes_above+1):
# We have to add 1 to notes_above since range is NOT inclusive of the ending term.
equal_temper.append(note_value(key, TET, i))
print(*equal_temper, sep="\n")
Some notes:
Log(x)/Log(B) gives log base B of X, no matter what the original base of Log is. This is why we divide by math.log(2) in our i_value function -- it gives us log base 2 of N/key.
The letter f used right before a string indicates a format string and will replace anything within {} with the whatever it evaluates to. For example, x=5; print(f"My x is {x}") will print My x is 5. This shorthand feature is new to python 3.6. If you're using a version prior to python 3.6, then you will need to replace the format statements with the longer version: print("Number of notes above = {}".format(notes_above))
The very last print statement at the end uses * to 'unpack' the list of equal_temper notes and then prints them with a newline (\n) between each element.
You could simply write something like
while lowest <= note <= highest:
...
That way you make the reverse of the condition in the if-statement the condition for the while loop and get rid of the hardcoded boolean value.
Edit:
I have made some slight modifications to your code and came up with this:
def get_equal_tempers(multiplier):
tempers = list()
note = 20
i = 1
while lowest <= note <= highest:
note = key * (2 ** (1 / TET)) ** (i*multiplier)
if note > highest or note < lowest:
break
tempers.append(note)
i += 1
return tempers
highest = 20000
lowest = 20
key = 440
TET = 12
equal_temper = [key]
equal_temper += get_equal_tempers(1)
equal_temper += get_equal_tempers(-1)
equal_tempered = sorted(equal_temper)
for i in range(len(equal_temper)):
print(equal_tempered[i])
When i run it on my machine, it produces the same output as your code. Can you please check?
Related
Write a function named powPosInt. This function will take two integer input parameters, named x and p. This function will compute the value of x to the power of p, where p >= 0, which is defined as x to the power of p = x × · · · × x
| {z }
p times
or 1 if p is 0.
The function will return this computed result. This function should not produce any console output.
For computing x to the power of p
, I am requiring that you implement the calculations using a while loop and an accumulator variable.
Examples of values you should test with: x = 2, p = 3 should return 8. x = 1, p = 4
should return 1. x = 7, p = 0 should return 1. x = −3, p = 2 should return 9.
x = −3, p = 3 should return −27.
This is what I have so far, I am having trouble figuring out how to put a while loop within this function. I am asking on here for help as my TEACHER will not assist, (he wants us to treat his class like the real world where we will have to figure out solutions on our own... why pay his salary right?)
def powPosInt(x,p):
number = p
count = 0
while (number != 0):
answer = (x**p)
count = count + 1
if (p<0):
answer = (x**(1/abs(p)))
return answer
Using accumulation means you need to create a variable that will store the result, and while loop will multiply it by x each step. You shouldn't use the ** operator.
steps = 0
result = 1
while (steps < p):
result *= x
steps += 1
def powPosInt(x,p):
number = p
count = 0
while (number != 0):
answer = (x**p)
count = count + 1
if (p<0):
answer = (x**(1/abs(p)))
return answer
I see a number of problems here:
A loop for calculating powers will be using repeated multiplication, not exponentiation. The intent here is to assume Python doesn't have an exponentiation operator, otherwise this assignment would be futile.
There's no reason to handle negative powers, the specifications clarly call for p >= 0. Hence your final if block is unnecessary.
This is a style issue only: you should get into the habit of using meaningful variable names. The only situation in which I use single-letter variables nowadays is a short-lived, non-nested, loop where the variable name is irrelevant (and I use i for that). For any other situation, I use explanatory names (yes, not even j or k for nested loops). And I'm even starting to move away from using i for these simple cases.
Having said all that, I'd suggest revisiting your solution to take those into account.
Once you've done so, you can refer to the code below for one solution:
def powPosInt(myBase, myPower):
result = 1
for _ in range(myPower):
result *= myBase
return result
And, if you wanted to go for extra credits(a), you could put in place contractual checks to ensure callers are doing the right thing:
def powPosInt(myBase, myPower):
if type(myBase) != int: raise TypeError("Base must be an integer")
if type(myPower) != int: raise TypeError("Power must be an integer")
if myPower < 1: raise ValueError("Power must be >= 1")
result = 1
for _ in range(myPower):
result *= myBase
return result
(a) Or risk losing marks for being a smart-alec. This depends very much on the personality of your teacher :-)
So far I have written this much
def find_num_min(xlst):
x = []
count = 0
w = 0
for i in range(len(xlst)):
if len(xlst) == 0:
return ()
else:
y = sorted(xlst)
w = y[0]
return w
You could do it like that, but python has many built in functions that can help you write shorter, more readable code:
def find(xlst):
smallest = min(xlst)
count = xlst.count(smallest)
print(f"The smallest number is {smallest}, it occurs {count} times.")
return smallest, count
In old boring inefficient longhand, just to show you what this could look like,
def find_num_min(xlst):
smallest = None
count = 0
for elt in xlst:
if not count or elt < smallest:
smallest = elt
count = 1
elif elt == smallest:
count += 1
return smallest, count
The condition if not count takes care to set smallest to something in the first iteration (and prevents us from attempting to compare elt to smallest when the latter is None, which woud cause Python to error out); in every subsequent iteration, count will be at least 1. (Go over this with pen and paper on a few simple example lists to make sure this is true.)
But definitely learn to look for existing code before writing your own. Both of the subtasks here are easily - and in many ways, better - done with existing functions from Python's standard library.
use numpy
import numpy as np
x = np.array(xlst)
minimum = x.min()
count = (x == minimum).sum()
using Counter
from collections import Counter
lst = [2,1,2,3,2,1,1,2,3,4,2,2]
c = Counter(lst)
lowest = min(c.keys())
print(f"lowest key: {lowest}, how many times: {c[lowest]}")
Using lambda function:
find_num_min = lambda xlst: (None, 0) if len(xlst) == 0 else (
(smallest := min(xlst)),
xlst.count(smallest)
)
Here is how a lambda function works, showing the same function in lambda form and normal function form:
# normal function
def func(param1, param2):
return param1 + param2
# lambda function
func = lambda (param1, param2): param1 + param2
Next, there are shortened if statements called ternary operators. These two statements are the same, as an example:
# normal if statements
if num > 0:
print("positive")
else:
print("negative")
# ternary
print("positive" if num > 0 else "negative")
The := operator is the same as the = operator, but it returns the value in addition to setting the value of whatever is on the left. Example:
print(n := 5) # prints 5 while setting n to 5
print(n) # also prints 5, as n was set to 5
This is useful because it means we don't have to find the minimum value twice.
Finally, the min function is built into Python and can be used to find the minimum value in an list. You can also use the count method on a list to find the number of times a value occurs in the list.
Putting all of these pieces together, we arrive at the final, optimized, one-line function at the beginning of this answer.
This is for a school assignment.
I have been tasked to define a function determining the largest square pyramidal number up to a given integer(argument). For some background, these are square pyramidal numbers:
1 = 1^2
5 = 1^2+2^2
14 = 1^2+2^2+3^2
So for a function and parameter largest_square_pyramidal_num(15), the function should return 14, because that's the largest number within the domain of the argument.
I get the idea. And here's my code:
def largest_square_pyramidal_num(n):
sum = 0
i = 0
while sum < n:
sum += i**2
i += 1
return sum
Logically to me, it seemed nice and rosy until I realised it doesn't stop when it's supposed to. When n = 15, sum = 14, sum < n, so the code adds one more round of i**2, and n is exceeded. I've been cracking my head over how to stop the iteration before the condition sum < n turns false, including an attempt at break and continue:
def largest_square_pyramidal_num(n):
sum = 0
for i in range(n+1):
sum += i**2
if sum >= n:
break
else:
continue
return sum
Only to realise it doesn't make any difference.
Can someone give me any advice? Where is my logical lapse? Greatly appreciated!
You can do the following:
def largest_pyr(x):
pyr=[sum([i**2 for i in range(1,k+1)]) for k in range(int(x**0.5)+1)]
pyr=[i for i in pyr if i<=x]
return pyr[-1]
>>>largest_pyr(15)
14
>>> largest_pyr(150)
140
>>> largest_pyr(1500)
1496
>>> largest_pyr(15000)
14910
>>> largest_pyr(150000)
149226
Let me start by saying that continue in the second code piece is redundant. This instruction is used for scenario when you don't want the code in for loop to continue but rather to start a new iteration (in your case there are not more instructions in the loop body).
For example, let's print every number from 1 to 100, but skip those ending with 0:
for i in range(1, 100 + 1):
if i % 10 != 0:
print(i)
for i in range(1, 100 + 1):
if i % 10 == 0:
# i don't want to continue executing the body of for loop,
# get me to the next iteration
continue
print(i)
The first example is to accept all "good" numbers while the second is rather to exclude the "bad" numbers. IMHO, continue is a good way to get rid of some "unnecessary" elements in the container rather than writing an if (your code inside if becomes extra-indented, which worsens readability for bigger functions).
As for your first piece, let's think about it for a while. You while loop terminates when the piramid number is greater or equal than n. And that is not what you really want (yes, you may end up with a piramid number which is equal to n, but it is not always the case).
What I like to suggest is to generate a pyramid number until in exceedes n and then take a step back by removing an extra term:
def largest_square_pyramidal_num(n):
result = 0
i = 0
while result <= n:
i += 1
result += i**2
result -= i ** 2
return result
2 things to note:
don't use sum as a name for the variable (it might confuse people with built-in sum() function)
I swapped increment and result updating in the loop body (such that i is up-to-date when the while loop terminates)
So the function reads like this: keep adding terms until we take too much and go 1 step back.
Hope that makes some sense.
Cheers :)
Algorithm question:
Let's say I want to determine whether a value is within a range (eg 2) of a tens multiple -- so, 8-12, 18-22, 28-32, etc.
My current solution is to add the range to the value, mod by 10, then re-subtract the range -- thus leaving me with something from -2 to 8 -- and then check whether the absolute value is less than the desired range.
value = 38
range = 2
cycle = 10
tweaked_mod = ((value + range) % cycle) - range
# tweaked_mod = -2
within_range = (abs(tweaked_mod) <= range)
# within_range = True
versus:
value = 37
range = 2
cycle = 10
tweaked_mod = ((value + range) % cycle) - range
# tweaked_mod = 7
within_range = (abs(tweaked_mod) <= range)
# within_range = False
It works, but it's awkward.
Am I missing a more intuitive / succinct algorithm here?
I find this solution easier to understand:
remainder = (value % cycle)
(remainder <= range) || (cycle - remainder) <= range
Basically I find the remainder of the value I search for in respect of the modulo (cycle) and then check if it is within the expected range.
Alternative:
An alternative solution (doing essentially the same) will be:
remainder = (value % cycle)
min(remainder, cycle - remainder) <= range
You are free to choose whichever of the two solutions you like better.
NOTE This algorithm works verbatim if range < cycle. In the other cases answer is always true.
I am trying to define a function that will include a variable n where n will be a string of numbers e.g. "3884892993", the definition of the function starts as is_true(n), however if n is going to be a string should it be is_true(n) and then once the string is defined I can test the function with an example string such as n = "3884892993". I get a syntax error when I use is_true(n) however. And I am just wondering how I would go about testing this function with an example string for n.
My entire function to define is shown here: http://oi44.tinypic.com/282i3qo.jpg but bear in mind I am an absolute novice so there will most probably be many mistakes, but I would appreciate some help from some experts if at all possible :)
def is_valid("n"): #n is the number to be checked.
number =
[int(y) for y in A] #converts the string into a list of useable digits.
altern1 = integer[-2::-2] #sets altern1 as one set of alternating digits.
double = [x*2 for x in altern1] #doubles each element of the list altern1.
sum1 = sum(double) # adds together all the doubled items of the list.
altern2 = integer[-1::-2] #sets altern2 as the other set of alternating digits.
return sum2 = sum(altern2)#sums the other set of alternating digits.
sumtotal = sum1 + sum2 #works out the total sum to be worked with.
for mod = sumtotal % 10: #works out remainder when sumtotal is divided by 10
if mod == 0 : #if remainder is zero sumtotal is a multiple of 10
print 'True' #sumtotal is a multiple of 10 therefore n is a credit card number
else:
print 'False' #sumtotal is NOT a multiple of 10 therefore not a valid credit card number
Here is the actual question:
The algorithm for verifying a number is as follows:
(a) Starting with the penultimate digit, and working towards the rst digit, double each alternating digit.
(b) Sum the doubled digits, treating 13 as 1+3, etc, and add the result to the sum of the undoubled
digits
(c) If the sum is divisible by 10 the number is a valid credit card number.
Write and test a function is_valid() which takes as an argument a credit card number as a string
(eg is valid("49927398716")) and returns True or False depending on whether the number is a
valid credit card number.
Quotes are only used for string literals, you wouldn't enclose a variable or parameter name in quotes to indicate that it will be a string. The function definition would look like:
def is_true(n):
And then in the body of the function you use n to reference the value that is passed in by the caller.
To call the function on a specific value, you do:
is_true("3884892993")
Side suggestion: Think of more explanatory names for your functions and variables. For instance, it seems like your function might be reasonably called is_valid_card_number.
I am not sure what is your question, but if you are trying to:
correctly define the function:
pay attention to the indentation (this is required by Python!),
see here for examples of function definitions,
convert a string variable into integer, you can do this:
new_var = int(old_var)
Generally please pay attention to types, because it is not like in some other dynamically typed languages and strings are not dynamically converted into numbers - you should do it explicitly.
read the value of the variable, based on its name:
my_var = vars().get('variable_name')
(where variable_name is the name of the variable and optionally you can give context within brackets after vars - see help(vars) for details)
Did any of the above solve your problem?
EDIT (based on the clarification):
This should solve your problem:
def is_true(my_variable):
# Here the variable named "my_variable" is accessible
If you want to do something "in-place" on the passed variable, I have a bad news: strings and integers are immutable in Python, thus you are not able to simply change them - you should probably return them as a result of the function (there are at least two workarounds, but I do not recommend them if you are a novice in Python).
EDIT (for proper code styling):
You should probably read PEP 8 to get familiar with what is the coding standard for Python scripts - this is commonly used across Python community and you should follow that (at some point you should appreciate it).
From the Wikipedia article on the Luhn algorithm:
def is_luhn_valid(cc):
num = map(int, str(cc))
return sum(num[::-2] + [sum(divmod(d * 2, 10)) for d in num[-2::-2]]) % 10 == 0
I have no idea what your function is supposed to do, but here are some remarks.
First of all, if you define the function then you use the following syntax
def is_true(n):
# do something
you can call this function like this is_true("3884892993"), i.e. you can pass string as n. Your function now need to treat variable n as a string. So you can use
number = [int(d) for d in n]
which will result in converting string into a list of digits.
One more remark: you used a return statement inside your is_true function. This statement will stop executing the function and return the value. Every code below return will never be executed.
May be like this. I leave your comments
def is_valid(n): #n is the number to be checked.
numbers = [int(y) for y in n] #converts the string into a list of useable digits.
double_alt = [sum([int(i) for i in str(x*2)]) for x in numbers[-2::-2]] #doubles and sum if more than 10each element of the list altern1.
sum1 = sum(double_alt) # adds together all the doubled items of the list.
sum2 = sum(numbers[-1::-2]) #sums the other set of alternating digits.
sumtotal = sum1 + sum2 #works out the total sum to be worked with.
return not sumtotal % 10
Here an implementation of the luhn algorithm that I had to make recently.
def is_valid_luhn(cc):
return not sum([sum(divmod(int(d) * 2, 10)) for d in cc[-2::-2]] + [int(d) for d in cc[-1::-2]]) % 10
# | double | |--- every -2th --| |--- every -1th --|
# |--------- step 1 -----------------|
# |------------- sum doubled digits --------------| |-- sum undoubled digits --|
# |---------------------- step 2: sum doubled/undoubled digits -----------------------|
# |-------------------------- step 3: sum % 10 == 0 --> not sum % 10 --------------------------|
Or if you'd like a more verbose version:
def is_valid_luhn(cc):
total = 0
# Double and sum every 2nd digit starting at -2.
for d in cc[-2::-2]:
# divmod(d*2, 10) returns (d*2 // 10, d*2 % 10)
# sum(divmod) return (d*2 // 10) + (d*2 % 10)
total += sum(divmod(int(d) * 2, 10))
# Sum every 2nd digit starting at -1.
for d in cc[-1::-2]:
total += int(d)
# Check module 10 of total: total % 10 == 0 --> not total % 10
return not total % 10