How to write a program with unspecified number of if-else - python

I have a little problem which looks like a “combinatorics” problem.
We know that 1+2+3+…+k = (k^2 + k)/2; so, let’s take the set of numbers S = {1,2,3,4,…,(k^2 + k)/2} and divide this collection into k parts:
The 1st part has 1 element 1; the 2nd has 2 elements 2,3; the 3rd has 3 elements 4,5,6; …and so on…; the kth having k elements (k^2 – k + 2)/2,…,(k^2 + k)/2.
Then I have to draw a random integer in S, say i = random.randint(1, (k^2 + k)/2) and I have to do some operations according to the element that was drawn:
if i == 1:
`something`
else if 2 <= i <= 3:
`something else`
else if 4 <= i <= 6:
`something else`
…
else: # last line when `i` is in the last `kth` part
`something else`
The number k I have to use is variable, so I can't actually write the above program, because I don't know a priori where it should stop...
It seems to me that the best would be to define a function:
def cases(k):
i = random.randint(1, (k^2 + k)/2)
if i == 1:
`something`
else if 2 <= i <= 3:
… and so on…
But the problem remains: how could I write such a function without a specific k? There may be a trick in Python to do this, but I don't see how.
All ideas will be welcome.

Sorry for the indenting; I acknowledge it's wrong. Following the comment of Zach Munro, I allow myself an answer to better clarify the idea that I had in mind and which was not very clear.
Below is the kind of program I was thinking of; the something and something else are actually similar, for they use the same function, but with a different domain each time:
def cases(start, end, k):
delta = (end - start) / k
i = random.randint(1, (k**2 + k)/2)
if i == 1:
# random in the 1st part
x = random.uniform(start, start + 1*delta)
elif 2 <= i <= 3:
# random in the 2nd part
x = random.uniform(start + 1*delta, start + 2*delta)
elif 4 <= i <= 6:
# random in the 3rd part
x = random.uniform(start + 2*delta, start + 3*delta)
...
elif (k**2 – k + 2)/2 <= i <= (k**2 + k)/2:
# random in the kth part
x = random.uniform(start + (k-1)*delta, start + k*delta)
The question is always how to stop using ... in the program, but to make something that runs when the parameters are provided (start is the beginning of an interval, end is the end of the interval and k is in fact the number of parts into which we separate this interval).
Basically, we sample more and more as we move away from the origin of the interval.

Related

What exactly is Stop in this question and how do I get the sum? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
Problem Statement
Edit: I have transcribed the image as suggested although I think some terms are better shown in the picture if anything is unclear here;
This function takes in a positive integer n and returns the sum of the following series Sn, as long as the absolute value of each term is larger than stop.
Sn= 1 − 1/2 + 1/3 − 1/4 + ... + (−1)n+1/n + ...
You can assume that stop is a float value and 0 < stop < 1.
You need not round the output.
For example, if stop = 0.249, then Sn is evaluated with only four terms.
Sn = 1 − 1/2 + 1/3 − 1/4
For example, if stop = 0.199, then Sn is evaluated with only five terms.
Sn = 1 − 1/2 + 1/3 − 1/4 + 1/5
The built-in function abs() is useful. You should use a while loop.
Test cases:
print( alternating_while(0.249) )
print( alternating_while(0.199) )
gives:
0.5833333333333333
0.7833333333333332
Now for this question, I want to get the sum of this series based on the conditions stipulated in the question.
My problem is I don't understand how to type the formula given in the question because I'm not familiar with how the while-loop works. Can someone instruct me on how to?
def alternating_while(stop):
total = 0
n = 1
term = 1
while abs(term) > stop:
total= (-1) ** (n + 1) / n + alternating_while(n - 1)
return total
No reason to use recursion as it wasn't mentioned as a requirement. Just check the term in the while loop for the stop condition:
Python 3.8+ (for the := operator):
def alternating_while(stop):
n = 1
total = 0
while abs(term := (-1)**(n+1)/n) > stop:
total += term
n += 1
return total
print(alternating_while(0.249))
print(alternating_while(0.199))
Output:
0.5833333333333333
0.7833333333333332
Pre-Python 3.8 version:
def alternating_while(stop):
n = 1
total = 0
while True:
term = (-1)**(n+1)/n
if abs(term) <= stop:
break
total += term
n += 1
return total
Or:
def alternating_while(stop):
n = 1
total = 0
term = (-1)**(n+1)/n
while abs(term) > stop:
total += term
n += 1
term = (-1)**(n+1)/n # redundant
return total
The key is "alternating". You can just increment the current denominator one at a time. If it is odd, you add. Otherwise, you subtract. abs is not really required; I'm not sure why they would mention it.
def alternating_while(stop):
total = 0
denom = 1
while 1/denom > stop:
if denom & 1:
total += 1/denom
else:
total -= 1/denom
denom += 1
return total
print(alternating_while(0.249))
print(alternating_while(0.199))
Output:
0.5833333333333333
0.7833333333333332
You need to cycle between adding and subtracting. The itertools module has a very helpful cycle class which you could utilise thus:
from itertools import cycle
from operator import add, sub
def get_term(d=2):
while True:
yield 1 / d
d += 1
def calc(stop=0.199):
c = cycle((sub, add))
term = get_term()
Sn = 1
while (t := next(term)) > stop:
Sn = next(c)(Sn, t)
return Sn
print(calc())
Output:
0.6936474305598223
Note:
The reference in the problem statement to absolute values seems to be irrelevant as no terms will ever be negative
I understand you need to use while in this particular problem, and this answer won't immediately help you as it is probably a few steps ahead of the current level of your course. The hope however is that you'll find it intriguing, and will perhaps come back to it in the future when you start being interested in performance and the topics introduced here.
from math import ceil
def f(stop):
n = ceil(1 / stop) - 1
return sum([(2 * (k & 1) - 1) / k for k in range(1, n + 1)])
Explanation
First, we want to establish ahead of time n, so that we avoid a math evaluation at each loop to decide whether to stop or not. Instead, the main loop is now for k in range(1, n + 1) which will go from 1 to n, included.
We use the oddness of k (k & 1) to determine the sign of each term, i.e. +1 for k == 1, -1 for k == 2, etc.
We make the series of terms in a list comprehension (for speed).
(A point often missed by many Pythonistas): building the list using such a comprehension and then summing it is, counter-intuitively, slightly faster than summing directly from a generator. In other words, sum([expr for k in generator]) is faster than sum(expr for k in generator). Note: I haven't tested this with Python 3.11 and that version of Python has many speed improvements.
For fun, you can change slightly the loop above to return the elements of the terms and inspect them:
def g(stop):
n = ceil(1 / stop) - 1
return [(2 * (k & 0x1) - 1, k) for k in range(1, n + 1)]
>>> g(.249)
[(1, 1), (-1, 2), (1, 3), (-1, 4)]

Python: Streamlining Code for Brown Numbers

I was curious if any of you could come up with a more streamline version of code to calculate Brown numbers. as of the moment, this code can do ~650! before it moves to a crawl. Brown Numbers are calculated thought the equation n! + 1 = m**(2) Where M is an integer
brownNum = 8
import math
def squareNum(n):
x = n // 2
seen = set([x])
while x * x != n:
x = (x + (n // x)) // 2
if x in seen: return False
seen.add(x)
return True
while True:
for i in range(math.factorial(brownNum)+1,math.factorial(brownNum)+2):
if squareNum(i) is True:
print("pass")
print(brownNum)
print(math.factorial(brownNum)+1)
break
else:
print(brownNum)
print(math.factorial(brownNum)+1)
brownNum = brownNum + 1
continue
break
print(input(" "))
Sorry, I don't understand the logic behind your code.
I don't understand why you calculate math.factorial(brownNum) 4 times with the same value of brownNum each time through the while True loop. And in the for loop:
for i in range(math.factorial(brownNum)+1,math.factorial(brownNum)+2):
i will only take on the value of math.factorial(brownNum)+1
Anyway, here's my Python 3 code for a brute force search of Brown numbers. It quickly finds the only 3 known pairs, and then proceeds to test all the other numbers under 1000 in around 1.8 seconds on this 2GHz 32 bit machine. After that point you can see it slowing down (it hits 2000 around the 20 second mark) but it will chug along happily until the factorials get too large for your machine to hold.
I print progress information to stderr so that it can be separated from the Brown_number pair output. Also, stderr doesn't require flushing when you don't print a newline, unlike stdout (at least, it doesn't on Linux).
import sys
# Calculate the integer square root of `m` using Newton's method.
# Returns r: r**2 <= m < (r+1)**2
def int_sqrt(m):
if m <= 0:
return 0
n = m << 2
r = n >> (n.bit_length() // 2)
while True:
d = (n // r - r) >> 1
r += d
if -1 <= d <= 1:
break
return r >> 1
# Search for Browns numbers
fac = i = 1
while True:
if i % 100 == 0:
print('\r', i, file=sys.stderr, end='')
fac *= i
n = fac + 1
r = int_sqrt(n)
if r*r == n:
print('\nFound', i, r)
i += 1
You might want to:
pre calculate your square numbers, instead of testing for them on the fly
pre calculate your factorial for each loop iteration num_fac = math.factorial(brownNum) instead of multiple calls
implement your own, memoized, factorial
that should let you run to the hard limits of your machine
one optimization i would make would be to implement a 'wrapper' function around math.factorial that caches previous values of factorial so that as your brownNum increases, factorial doesn't have as much work to do. this is known as 'memoization' in computer science.
edit: found another SO answer with similar intention: Python: Is math.factorial memoized?
You should also initialize the square root more closely to the root.
e = int(math.log(n,4))
x = n//2**e
Because of 4**e <= n <= 4**(e+1) the square root will be between x/2 and x which should yield quadratic convergence of the Heron formula from the first iteration on.

3SUM with reversed outer loop

There is known problem called 3SUM.
Here is a little altered question: Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target.
I'm trying to write code for it on two different sites with code-challenge problems. If I write outer loop from 0 and forward (as in wiki) I get accepted result on both sites. But if I write code as below (difference in outer loop and settings of "j" and "k") than on one of them I get wrong answer on some test case (I don't know it).
Could you tell me please if there is a problem in my code? It would be awesome to get input which causes wrong answer if code is really has mistake.
def threeSumClosest(self, A, B):
A.sort()
closest = 1000000000
if len(A) < 3:
return closest
for i in xrange(len(A)-1, 1, -1):
j = 0
k = i - 1
while j < k:
S = A[i] + A[j] + A[k]
if S == B:
return B
elif S > B:
if closest is None or abs(B - S) < abs(B - closest):
closest = S
k -= 1
else:
if closest is None or abs(B - S) < abs(B - closest):
closest = S
j += 1
return closest
There is nothing wrong with your code, I think the test case where your code fails is a case where two right answers exist. Consider the following example:
A = {1, 2, 3, 6, 10} and B = 16
In this case, if you run the code with your current settings (outer loop starting from the end), then your code will output 17 which is 10 + 6 + 1; however, with the "wiki" settings (outer loop starting from the beginning), the output would be 15 which is 10 + 2 + 3. In both cases the difference between the answer and B is 1, but the numbers are different.
This could be easily checked: in your code, change the two conditions of abs(B - S) < abs(B - closest) to abs(B - S) <= abs(B - closest) (inside the elif and else statements). This will make sure that you would get the same answer as if you would have started from the beginning instead of the end in the outer loop. Make these two changes and resubmit your code, it should take care of it.

SPOJ The Next Palindrome

I am trying to solve SPOJ problem 5: find the next largest integer "palindrome" for a given input; that is, an integer that in decimal notation reads the same from left-to-right and right-to-left.
Please have a look of the question here
Instead of using brute force search, I try to calculate the next palindrome. But my code still returns TLE (that is, Time Limit Exceeded) and I am frustrated... Would you mind giving me a hint?
Here is my code in python 3.x
if __name__ == '__main__':
n = int(input())
for i in range(n):
string = input()
length = len(string)
ans = ""
if length %2 == 0 :
half = length // 2
str_half = string[0:half]
ans = str_half + str_half[::-1]
if(ans <= string):
str_half = str(int(str_half) + 1)
ans = str_half + (str_half[0:half])[::-1]
print(ans)
else:
half = length // 2
str_half = string[0:half]
ans = str_half + string[half] + str_half[::-1]
if(ans<= string):
str_half = str(int(str_half+string[half]) + 1)
ans = str_half + (str_half[0:half])[::-1]
print(ans)
The input can be long. The problem statement says "not more than 1000000 digits". So probably there are a couple of test cases with several hundred thousand digits. Splitting such a string in halves, reversing one half and appending them does take a little time. But as far as I know, Python's string handling is pretty good, so that's only a small contribution to the problem.
What is taking a long time is converting strings of such length to numbers and huge numbers to strings. For K = 10 ** 200000 + 2, the step str_half = str(int(str_half+string[half]) + 1) alone took almost a second here. It may be faster on your computer, but SPOJ's machines are quite slow, one such occurrence may push you over the time limit there.
So you have to avoid the conversions, work directly on the string representations (mutable lists of digits).
So based on that problem lets figure out what the longest palindrome is for the case where K.length == 1 . This case can be safely ignored as there is no value larger than K that is a palindrome of K . The same applies to K.length == 2 . Therefore the pseudocode to evaluate this looks as follows:
if K.length <= 2
pass
When K.length == 3 the values we care about are K[0] and K[2] this gives us the boundaries. For example K == 100 . the values we care about are 1 and 0 . If K[0] is larger than K[2] we know that we must make K[2] = K[0] and we are done. Another example is K == 200 the first value larger is 202 which is the first prime that is equal. If K[0] == K[2] and K < 999, we increment K[1] by 1 and we are done. Pseudocode as follows:
if K[0] > K[2]
K[2] = K[0]
if K[0] == K[2] and K < 999
K[1]++
If all values in K are 9 (999,9999, etc) increment K by 2 and end the process.
I will leave the general form of the algorithm to you, unless you are ultimately stuck.

Optimizing python code

Any tips on optimizing this python code for finding next palindrome:
Input number can be of 1000000 digits
COMMENTS ADDED
#! /usr/bin/python
def inc(lst,lng):#this function first extract the left half of the string then
#convert it to int then increment it then reconvert it to string
#then reverse it and finally append it to the left half.
#lst is input number and lng is its length
if(lng%2==0):
olst=lst[:lng/2]
l=int(lng/2)
olst=int(olst)
olst+=1
olst=str(olst)
p=len(olst)
if l<p:
olst2=olst[p-2::-1]
else:
olst2=olst[::-1]
lst=olst+olst2
return lst
else:
olst=lst[:lng/2+1]
l=int(lng/2+1)
olst=int(olst)
olst+=1
olst=str(olst)
p=len(olst)
if l<p:
olst2=olst[p-3::-1]
else:
olst2=olst[p-2::-1]
lst=olst+olst2
return lst
t=raw_input()
t=int(t)
while True:
if t>0:
t-=1
else:
break
num=raw_input()#this is input number
lng=len(num)
lst=num[:]
if(lng%2==0):#this if find next palindrome to num variable
#without incrementing the middle digit and store it in lst.
olst=lst[:lng/2]
olst2=olst[::-1]
lst=olst+olst2
else:
olst=lst[:lng/2+1]
olst2=olst[len(olst)-2::-1]
lst=olst+olst2
if int(num)>=int(lst):#chk if lst satisfies criteria for next palindrome
num=inc(num,lng)#otherwise call inc function
print num
else:
print lst
I think most of the time in this code is spent converting strings to integers and back. The rest is slicing strings and bouncing around in the Python interpreter. What can be done about these three things? There are a few unnecessary conversions in the code, which we can remove. I see no way to avoid the string slicing. To minimize your time in the interpreter you just have to write as little code as possible :-) and it also helps to put all your code inside functions.
The code at the bottom of your program, which takes a quick guess to try and avoid calling inc(), has a bug or two. Here's how I might write that part:
def nextPal(num):
lng = len(num)
guess = num[:lng//2] + num[(lng-1)//2::-1] # works whether lng is even or odd
if guess > num: # don't bother converting to int
return guess
else:
return inc(numstr, n)
This simple change makes your code about 100x faster for numbers where inc doesn't need to be called, and about 3x faster for numbers where it does need to be called.
To do better than that, I think you need to avoid converting to int entirely. That means incrementing the left half of the number without using ordinary Python integer addition. You can use an array and carry out the addition algorithm "by hand":
import array
def nextPal(numstr):
# If we don't need to increment, just reflect the left half and return.
n = len(numstr)
h = n//2
guess = numstr[:n-h] + numstr[h-1::-1]
if guess > numstr:
return guess
# Increment the left half of the number without converting to int.
a = array.array('b', numstr)
zero = ord('0')
ten = ord('9') + 1
for i in range(n - h - 1, -1, -1):
d = a[i] + 1
if d == ten:
a[i] = zero
else:
a[i] = d
break
else:
# The left half was all nines. Carry the 1.
# Update n and h since the length changed.
a.insert(0, ord('1'))
n += 1
h = n//2
# Reflect the left half onto the right half.
a[n-h:] = a[h-1::-1]
return a.tostring()
This is another 9x faster or so for numbers that require incrementing.
You can make this a touch faster by using a while loop instead of for i in range(n - h - 1, -1, -1), and about twice as fast again by having the loop update both halves of the array rather than just updating the left-hand half and then reflecting it at the end.
You don't have to find the palindrome, you can just generate it.
Split the input number, and reflect it. If the generated number is too small, then increment the left hand side and reflect it again:
def nextPal(n):
ns = str(n)
oddoffset = 0
if len(ns) % 2 != 0:
oddoffset = 1
leftlen = len(ns) / 2 + oddoffset
lefts = ns[0:leftlen]
right = lefts[::-1][oddoffset:]
p = int(lefts + right)
if p < n:
## Need to increment middle digit
left = int(lefts)
left += 1
lefts = str(left)
right = lefts[::-1][oddoffset:]
p = int(lefts + right)
return p
def test(n):
print n
p = nextPal(n)
assert p >= n
print p
test(1234567890)
test(123456789)
test(999999)
test(999998)
test(888889)
test(8999999)
EDIT
NVM, just look at this page: http://thetaoishere.blogspot.com/2009/04/finding-next-palindrome-given-number.html
Using strings. n >= 0
from math import floor, ceil, log10
def next_pal(n):
# returns next palindrome, param is an int
n10 = str(n)
m = len(n10) / 2.0
s, e = int(floor(m - 0.5)), int(ceil(m + 0.5))
start, middle, end = n10[:s], n10[s:e], n10[e:]
assert (start, middle[0]) == (end[-1::-1], middle[-1]) #check that n is actually a palindrome
r = int(start + middle[0]) + 1 #where the actual increment occurs (i.e. add 1)
r10 = str(r)
i = 3 - len(middle)
if len(r10) > len(start) + 1:
i += 1
return int(r10 + r10[-i::-1])
Using log, more optized. n > 9
def next_pal2(n):
k = log10(n + 1)
l = ceil(k)
s, e = int(floor(l/2.0 - 0.5)), int(ceil(l/2.0 + 0.5))
mmod, emod = 10**(e - s), int(10**(l - e))
start, end = divmod(n, emod)
start, middle = divmod(start, mmod)
r1 = 10*start + middle%10 + 1
i = middle > 9 and 1 or 2
j = s - i + 2
if k == l:
i += 1
r2 = int(str(r1)[-i::-1])
return r1*10**j + r2

Categories