In the following code snippet I am finding the sum of digits of all odd number between the interval [a,b]
def SumOfDigits(a, b):
s = 0
if a%2 == 0:
a+=1
if b%2 == 0:
b-=1
for k in range(a,b+1,2):
s+= sum(int(i) for i in list(str(k)))
return s
Is there an efficient way to accomplish the same?
Any patterns, which is leading to a clear cut formula.
I did search in https://oeis.org
Avoid all the overhead of conversion to and from strings and work directly with the numbers themselves:
def SumOfDigits(a, b):
result = 0
for i in range(a + (not a % 2), b + 1, 2):
while i:
result += i % 10
i //= 10
return result
Of course there is a pattern. Let's look at the problem of summing up the digitsums of all – odd and even – numbers between a and b inclusively.
For example: 17 to 33
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
The middle section gives you the sum of all digits from 0 to 9 (45) plus ten times 2. The left section is 7 + 8 + 9 plus three times 1 and the right the sum of 0 + 1 + 2 + 3 plus four times 3.
The middle section can comprise several blocks of ten, for example if you calculate the range between 17 and 63, you get 40 times 45 plus ten simes the the digitsums from 2 to 5.
This gives you a recursive algorithm:
def ssum(n):
return n * (n + 1) // 2
def dsum(a, b):
res = 0
if a == b:
while a:
res += a % 10
a //= 10
elif a < b:
aa = a // 10
bb = b // 10
ra = a % 10
rb = b % 10
if aa == bb:
res += ssum(rb) - ssum(ra - 1)
res += (rb - ra + 1) * dsum(aa, bb)
else:
if ra > 0:
res += 45 - ssum(ra - 1)
res += (10 - ra) * dsum(aa, aa)
aa += 1
if rb < 9:
res += ssum(rb)
res += (rb + 1) * dsum(bb, bb)
bb -= 1
if aa <= bb:
res += 45 * (bb - aa + 1)
res += 10 * dsum(aa, bb)
return res
Now let's extend this to include only odd numbers. Adkust a so that it is even and b so that it is odd. Your sum of digit sums now runs over pairs of even and odd numbers, where even + 1 == odd. That means that the digitsum of the odd number id one more than the even number, because all except the last digits are the same and the last odd digit is one more than the even digit.
Therefore:
dsum(a, b) == oddsum + evensum
and:
oddsum - evensum == (b - a + 1) // 2
The function to sum the digitsums of all odd numbers is then:
def oddsum(a, b):
if a % 2: a -= 1
if b % 2 == 0: b -= 1
d = (b - a + 1) // 2
return (dsum(a, b) + d) // 2
When I looked at your comment about OEIS, I've noticed that the algorithm can probably be simplified by writing a function to sum the digits from all numbers from zero to n and then calculate the difference dsum(b) - dsum(a). There are probably more opportunities for optimisation.
Related
Hi I have task that that I dont know how to do. Basically I need to write primal numberswhere is only difference by 2 numbers, only primal number which is either two more or two less than another prime number and then print every pair of these primes that are less than or equal to 100.
Here is how it should look like.
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73
Here is my code.I don't know how to eliminate these that are more than 2
for b in range(1, n):
if b>2:
for p in range(2, b):
if (b%p) ==0:
break
else:
print(b )
Without changing your existing code, you could do:
lastprime = 2
for b in range(1, n):
if b>2:
for p in range(2, b):
if (b%p) ==0:
break
else:
if b == lastprime + 2:
print(lastprime, b)
lastprime = b
It should be a bit more efficient by using Sieve of Eratosthenes:
sieve = [True for _ in range(n + 1)]
current_number = 2
while (current_number * current_number <= n):
if (sieve[current_number] == True):
for i in range(current_number * current_number, n + 1, current_number):
sieve[i] = False
current_number += 1
primes = [index for index in range(2, n + 1) if sieve[index]]
for i in range(len(primes) - 1):
first = primes[i]
second = primes[i + 1]
if first + 2 == second:
print(f'{first} {second}')
or even more efficient:
sieve = [False] * 2 + [True] * (n - 1)
for number in range(int(n ** 0.5 + 1.5)):
if sieve[number]:
for i in range(number * number, n + 1, number):
sieve[i] = False
primes = [i for i, prime in enumerate(sieve) if prime]
for i, first in enumerate(primes[:-1]):
second = primes[i + 1]
if first + 2 == second:
print(f'{first} {second}')
Explanation:
https://www.geeksforgeeks.org/sieve-of-eratosthenes/
Is there a way to list super primes (primes in a prime position wikipedia article) between 1 and n using NumPy.
I got this without Numpy, is that okay?
here is the code based on
Sieve of Atkin
import math
is_prime = list()
limit = 100
for i in range(5, limit):
is_prime.append(False)
for x in range(1, int(math.sqrt(limit)) + 1):
for y in range(1, int(math.sqrt(limit)) + 1):
n = 4 * x ** 2 + y ** 2
if n <= limit and (n % 12 == 1 or n % 12 == 5) and n <= len(is_prime):
# print "1st if"
is_prime[n] = not is_prime[n]
n = 3 * x ** 2 + y ** 2
if n <= limit and n % 12 == 7:
# print "Second if"
is_prime[n] = not is_prime[n]
n = 3 * x ** 2 - y ** 2
if x > y and n <= limit and n % 12 == 11:
# print "third if"
is_prime[n] = not is_prime[n]
for n in range(5, int(math.sqrt(limit))):
if is_prime[n]:
for k in range(n ** 2, limit + 1, n ** 2):
if k <= len(is_prime):
is_prime[k] = False
for n in range(5, limit):
if n < len(is_prime) and is_prime[n]:
print(n)
Numpy Code Version
Sieve modification of Numpy Sieve by user2357112 supports Monica
import numpy as np
def sieve(n):
'''
Sieve of Erastosenes using numpy
'''
flags = np.ones(n, dtype=bool) # Set all values to True
# Set 0, 1, and even numbers > 2 to False
flags[0] = flags[1] = False
flags[4:n:2] = False
for i in range(3, n, 2):
# Check for odd primes
if flags[i]:
flags[i*i::i] = False
return np.flatnonzero(flags) # Indexes of True are prime
def superPrimes(n):
'''
Find superprimes
'''
# Find primes up to n using sieve
p = sieve(n)
# p(i) denotes the ith prime number, the numbers in this sequence are those of the form p(p(i))
# Want to find numbers we can index in the list (i.e. has to be less than len(p))
indexes = p[p < len(p)] # base 1 indexes within range
return p[indexes-1] # Subtract 1 since primes array is base 0 indexed
Test
print(superPrimes(200))
# Output: [ 3 5 11 17 31 41 59 67 83 109 127 157 179 191]
i can't understand why the output of my code concatnates the digits instead of showing their sum:
#Get a number, and show the number of digits and the sum of the digits.
num = int(input('Enter a number: '))
j = 0
i = 1
k = 0
while i < num:
i = i*10
j += 1
k += (num - k)%i
print (f' The number has {j} digit(s), and the sum is: {k}')
Follow the code. Let's say num = 432:
i = 1 * 10 = 10
j = 0 + 1 = 1
k = 0 + (432 - 0)%10 = 2
---
i = 10 * 10 = 100
j = 1 + 1 = 2
k = 2 + (432 - 2)%100 = 2 + 32 = 34
---
i = 100 * 10 = 1000
j = 2 + 1 = 3
k = 34 + (432 - 34)%1000 = 34 + 398 = 432
This algorithm is most definitely not adding every digit. There are several ways to do what you intend in python. One way is inputting the number as a string and summing every digit casting them as integers inside a generator:
num = input('Enter a number: ')
total = sum(int(digit) for digit in num)
print(total)
If you want the number to be an integer since the beginning, you can also do this:
num = int(input('Enter a number: '))
total = 0
while num > 0:
digit = num%10
total += digit
num /= 10 # num //= 10 in python 3
print(total)
def sum(a):
if a==1:
s=1
else:
s=1+2*sum(a-1)
return s
function:calculate the sum of the sequence of number. Its common ratio is 2, last term is 2^( a-1) and first term is 1.
Why does it use s=1+2*sum(a-1) to implement the function?
def sum1(a):
if a==1:
s=1
else:
s=1+2*sum1(a-1)
return s
What this function does, let's take a=4.
(1) s = 1 + 2*sum1(4-1) = 1 + 2*sum1(3) = 1 + 2*s2
(2) s2 = 1 + 2*sum1(3-1) = 1 + 2*sum1(2) = 1 + 2*s3
(3) s3 = 1 + 2*sum(2-1) = 1 + 2*sum(1) = 1 + 2*s4 = 1+2 = 3
Going BackWard : (3 * 2 + 1 ) * 2 + 1 = (7) * 2 + 1 = 15
Do it for bigger numbers, you will notice that this is the formula of 2^a - 1.
Print 2^a and 2^a - 1
Difference: (4, 3)
Difference: (8, 7)
Difference: (16, 15)
Difference: (32, 31)
Difference: (64, 63)
Difference: (128, 127)
Difference: (256, 255)
#Zhongyi I'm understanding this question as asking "how does recursion work."
Recursion exists in Python, but I find it a little difficult to explain how it works in Python. Instead I'll show how this works in Racket (a Lisp dialect).
First, I'lll rewrite yoursum provided above into mysum:
def yoursum(a):
if a==1:
s=1
else:
s=1+2*yoursum(a-1)
return s
def mysum(a):
if a == 1:
return 1
return 1 + (2 * mysum(a - 1))
for i in range(1, 11):
print(mysum(i), yoursum(i))
They are functionally the same:
# Output
1 1
3 3
7 7
15 15
31 31
63 63
127 127
255 255
511 511
1023 1023
In Racket, mysum looks like this:
#lang racket
(define (mysum a)
(cond
((eqv? a 1) 1)
(else (+ 1 (* 2 (mysum (sub1 a)))))))
But we can use language features called quasiquoting and unquoting to show what the recursion is doing:
#lang racket
(define (mysum a)
(cond
((eqv? a 1) 1)
(else `(+ 1 (* 2 ,(mysum (sub1 a))))))) ; Notice the ` and ,
Here is what this does for several outputs:
> (mysum 1)
1
> (mysum 2)
'(+ 1 (* 2 1))
> (mysum 3)
'(+ 1 (* 2 (+ 1 (* 2 1))))
> (mysum 4)
'(+ 1 (* 2 (+ 1 (* 2 (+ 1 (* 2 1))))))
> (mysum 5)
'(+ 1 (* 2 (+ 1 (* 2 (+ 1 (* 2 (+ 1 (* 2 1))))))))
You can view the recursive step as substituting the expression (1 + 2 * rest-of-the-computation)
Please comment or ask for clarification if there are parts that still do not make sense.
Formula Explanation
1+2*(sumOf(n-1))
This is not a generic formula for Geometric Progression.
this formula is only valid for the case when ratio is 2 and first term is 1
So how it is working.
The Geometric Progression with first term = 1 and r = 2 will be
1,2,4,6,8,16,32,64
FACT 1
Here you can clearly see nth term is always equals to sumOf(n-1) terms + 1
Let declare an equation from fact 1
n = sumOf(n-1) + 1 =======> Eq1.
Let put our equation to test
put n = 2 in Eq1
2 = sumOf(2-1) + 1
we know that sumOf(1) is 1 then
2 = 2 ==> proved
so if n = sumOf(n-1)+1 then
FACT 2
Sum of n term is n term + sum(n-1) terms
Lets declare an Equation from FACT 2
sumOf(n) = sumOf(n-1) + n ==> eq2
let us put eq1 in eq2 i.e. n = sumOf(n-1) + 1
sumOf(n) = sumOf(n-1) + sumOf(n-1) + 1 ==> eq3
Simplifying
sumOf(n) = 2 * sumOf(n-1) + 1
Rearranging
sumOf(n) = 1 + 2 * sumOf(n-1) ==> final Equation
Now Code this equation
we know sumOf 1st term is alway 1 so, this is our base case.
def sumOf(a):
if a==1:
return 1
so now sum of first n terms will be 1 + 2 * sumOf(n-1) ==> From final Equation
put this equation in else part
def sumOf(a):
if a==1:
return 1
else:
return 1 + 2 * sumOf(a-1)
This is meant to calculate the smallest common multiple of 1-20.
I can't seem to figure out why the while loop won't end. Can anyone help me out?
i = 1
j = 1
factors = 0
allfactor = False
while allfactor == False:
while j < 21:
if i % j == 0:
factors = factors + 1
j = j + 1
else:
break
if factors == 20:
allfactor = True
break
else:
i = i + 1
j = 1
factors = 0
The least common multiple of the numbers 1..20 is 232792560.
To get to that number you just need to look at the numbers 1 to 20. You need at least all prime numbers: 2, 3, 5, 7, 11, 13, 17 and 19.
In addition, you need another 2 to calculate 4, and two more 2s for 16. To get a 9 you also need another 3.
So you end up with:
2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13 * 17 * 19 = 232792560
And you can easily confirm that using Python:
>>> all(map(lambda x: 232792560 % x == 0, range(1, 21)))
True
I.e. all numbers are divisors of said number; and per the proof above (its construction) there is no smaller number.