Volume of pile of cubes - python

I'm trying a challenge. The idea is the following:
"Your task is to construct a building which will be a pile of n cubes.
The cube at the bottom will have a volume of n^3, the cube above will
have volume of (n-1)^3 and so on until the top which will have a
volume of 1^3.
You are given the total volume m of the building. Being given m can
you find the number n of cubes you will have to build? If no such n
exists return -1"
I saw that apparently:
2³ + 1 = 9 = 3² and 3 - 1 = 2
3³ + 2³ + 1 = 36 = 6² and 6 - 3 = 3
4³ + 3³ + 2³ + 1 = 100 = 10² and 10 - 6 = 4
5³ + 4³ + 3³ + 2³ + 1 = 225 = 15² and 15 - 10 = 5
6³ + 5³ + 4³ + 3³ + 2³ + 1 = 441 = 21² and 21 - 15 = 6
So if I thought, if I check that a certain number is a square root I can already exclude a few. Then I can start a variable at 1 at take that value (incrementing it) from the square root. The values will eventually match or the former square root will become negative.
So I wrote this code:
def find_nb(m):
x = m**0.5
if (x%1==0):
c = 1
while (x != c and x > 0):
x = x - c
c = c + 1
if (x == c):
return c
else:
return -1
return -1
Shouldn't this work? What am I missing?
I fail a third of the sample set, per example: 10170290665425347857 should be -1 and in my program it gives 79863.
Am I missing something obvious?

You're running up against a floating point precision problem. Namely, we have
In [101]: (10170290665425347857)**0.5
Out[101]: 3189089316.0
In [102]: ((10170290665425347857)**0.5) % 1
Out[102]: 0.0
and so the inner branch is taken, even though it's not actually a square:
In [103]: int((10170290665425347857)**0.5)**2
Out[103]: 10170290665425347856
If you borrow one of the many integer square root options from this question and verify that the sqrt squared gives the original number, you should be okay with your algorithm, at least if I haven't overlooked some corner case.
(Aside: you've already noticed the critical pattern. The numbers 1, 3, 6, 10, 15.. are quite famous and have a formula of their own, which you could use to solve for whether there is such a number that works directly.)

DSM's answer is the one, but to add my two cents to improve the solution...
This expression from Brilliant.org is for summing cube numbers:
sum of k**3 from k=1 to n:
n**2 * (n+1)**2 / 4
This can of course be solved for the total volume in question. This here is one of the four solutions (requiring both n and v to be positive):
from math import sqrt
def n(v):
return 1/2*(sqrt(8*sqrt(v) + 1) - 1)
But this function also returns 79863.0. Now, if we sum all the cube numbers from 1 to n, we get a slightly different result due to the precision error:
v = 10170290665425347857
cubes = n(v) # 79863
x = sum([i**3 for i in range(cubes+1)])
# x = 10170290665425347857, original
x -> 10170290665425347856

I don't know if your answer is correct, but I have another solution to this problem which is waaaay easier
def max_level(remain_volume, currLevel):
if remain_volume < currLevel ** 3:
return -1
if remain_volume == currLevel ** 3:
return currLevel
return max_level(remain_volume - currLevel**3, currLevel + 1)
And you find out the answer with max_level(m, 0). It takes O(n) time and O(1) memory.

I have found a simple solution over this in PHP as per my requirement.
function findNb($m) {
$total = 0;
$n = 0;
while($total < $m) {
$n += 1;
$total += $n ** 3;
}
return $total === $m ? $n : -1;
}
In Python it would be:
def find_nb(m):
total = 0
n = 0
while (total < m):
n = n + 1
total = total + n ** 3
return n if total == m else -1

Related

Python: calculate (1/1) + (1 + 2) / 2 + ... + (1 + 2 + ... + n) / n [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 10 months ago.
Improve this question
The use of for loops is not allowed. This program is designed to calculate values up to number 'n' which is a value entered by the user. Help on this would be appreciated. A photo of the problem and my code so far is listed below:
import math
counterN = 0 #Numerator
counterD = 0 #Denominator
divNumber = 0 #Numerator/Denominator
nValue = 0 #Variable to add numberator values to
dValue = 0 #Variable to add denominaor values to
#Prompts user for a
print("Please enter a value for calculation: ")
number = int(input())
if (number <= 0):
print("Invalid input given.")
#Adds the numerator up to number entered by user
while (counterN <= number):
counterN = counterN + 1
nValue += counterN
#Denominator calculation
counterD = counterD + 1
dValue += counterD
divNumber = nValue / dValue
divNumber += divNumber
#Outputs value to user
print("The result is" , divNumber)
print(counterN, nValue)
You can use recursion for such problems. I've written a recursive solution for you which I found very intuitive-
global SUM
SUM = 0
def fracSUMCalc(n):
if n == 1:
return 1
SUM = fracSUMCalc(n-1) + (sum(range(1, n+1)) / n)
return SUM
print(fracSUMCalc(n=998))
This prints out 249749.5 which is the answer to the sum of your series till n=998. You can vary n as per your need.
Please note that this solution will work fine on any standard modern day laptop till n=998. For n>998, you'd either have to increase your machine's recursion depth limit or use a different approach to develop a more efficient program.
There is a solution that just uses no loops or recursion at all, just maths
def frac_series_sum(n):
return n + sum(range(n)) * 0.5
print(frac_series_sum(1)) # 1.0
print(frac_series_sum(5)) # 10.0
print(frac_series_sum(100)) # 2575.0
It can be shown (see below) that your sum is equal to
for any natural number n.
Thus, this function calculates the sum you want. It does not involve any recursion, for loops or any kind of iteration (it is O(1)):
def my_calc(n):
"Returns 1/1 + (1 + 2)/2 + ... + (1 + 2 + ... + n)/n"
return 0.25 * n * (n + 3)
This is as efficient as it gets.
If you are not allowed to use the solution above and you need to use a while loop:
def my_inefficient_calc(n):
"Returns 1/1 + (1 + 2)/2 + ... + (1 + 2 + ... + n)/n"
result = 0
i = 1
while (i <= n):
result += (sum(range(1, i + 1))) / i
i += 1
return result
If you are not allowed to use the built-in sum function, you can calculate the sum using a nested while loop:
def even_less_efficient(n):
"Returns 1/1 + (1 + 2)/2 + ... + (1 + 2 + ... + n)/n"
result = 0
i = 1
while (i <= n):
inner_sum = 0
k = 1
while (k <= i):
inner_sum += k
k += 1
result += inner_sum / i
i += 1
return result
i is a loop variable (counter) for the outer loop. It ranges from i = 1 up to n.
k is a loop variable (counter) for the inner loop. It ranges from k = 1 up to i.
The inner loop is responsible for calculating the sum in the numerator for each term. This sum is stored in inner_sum.
Once the sum is calculated for a given i (i.e. once we are done with the inner loop), we divide this sum by i to get one of the terms in the mathematical expression.
The outer loop is responsible for summing all of the terms from i = 1 up to n.
Intuitive Proof
How did I arrive at this?
You want a program that calculates the following sum:
This is an elegant solution that has stuck with me for a long time that I will never forget. I once learned in real analysis of a famous mathematician who at the age of 5 (if I remember correctly) reasoned that the sum of i from i = 1 up to k is:
He did so by writing out the sum twice, but the second in reverse order:
(sum of i from i = 1 to k) = 1 + 2 + ... + (k - 1) + k
(sum of i from i = 1 to k) = k + (k - 1) + ... + 2 + 1
----------------------------------------------------------
(k+1)+ (k+1) +... + (k+1) + (k+1) # k terms
He noticed that each sum has k numbers and the sum of each column is equal to k + 1. So, if you add the two sums, you get
2 * (sum of i from i = 1 to k) = k * (k + 1)
Thus
(sum of i from i = 1 to k) = (k * (k + 1)) / 2
which is the same as the result in the second image above.
Substitution of this result into the left-hand-side of the expression in the first image:
Now apply the result that we derived again to the last expression above:
Thus
or
0.25 * n * (n + 3)
Note: A formal proof of the above result would be a proof by induction to show it is true for any natural number n. This proof by induction part is the easy part. I have omitted such a proof as the above should be obvious to anyone that sees it.
Bro, I just did easier
counterN = 1 #Numerator
result = []
#Prompts user for a
print("Please enter a value for calculation: ")
number = int(input())
if (number <= 0):
print("Invalid input given.")
#Adds the numerator up to number entered by user
while (counterN <= number):
numerator = sum(range(counterN+1))
denominator = counterN
result.append(numerator / denominator)
#solucion
counterN += 1
#Outputs value to user
print("The result is" , sum(result))

Improving performance of finding out how many possible triangles can be made with a given stick

I am doing an assessment that is asking by the given "n" as input which is a length of a stick; how many triangles can you make? (3 < n < 1,000,000)
For example:
input: N=8
output: 1
explanation:
(3,3,2)
input: N=12
output: 3
explanation:
(4,4,4) (4,5,3) (5,5,2)
Now the codes I wrote are returning 33 % accuracy as the web assessment is throwing time limit error.
ans = 0
n = int(input())
for a in range(1, n + 1):
for b in range(a, n - a + 1):
c = n - a - b
if a + b > c >= b:
ans += 1
print(ans)
code b:
ans = 0
n = int(input())
for i in range(1,n):
for j in range(i,n):
for c in range(j,n):
if(i+j+c==n and i+j>c):
ans+=1
print(ans)
How can this be made faster?
This is an intuitive O(n) algorithm I came up with:
def main():
n = int(input())
if n < 3:
print(0)
return
ans = n % 2
for a in range(2, n//2+1):
diff = n - a
if diff // 2 < a:
break
if diff % 2 == 0:
b = diff // 2
else:
b = diff // 2 + 1
b = max(b - a // 2, a)
c = n - b - a
if abs(b - c) >= a:
b += 1
c -= 1
ans += abs(b-c)//2 + 1
print(ans)
main()
I find the upper bound and lower bound for b and c and count the values in that range.
I thought of a completely different way to do it:
We take the smallest side and call it a. It can never be more than n/3, otherwise a different side would be the smallest.
We try to figure out what is the next smallest side (b):
We see what's left after reducing our a.
We divide it by 2 in order to find the middle where we'll start advancing from
We'll see how far we can get before the difference between the lengths is a (or the difference from the middle is a/2) as that's the minimum b side length that is possible and satisfies a+b>c. Basically, the second smallest side is a/2 less than the middle.
The smallest side is the maximum between our calculation or a, in caseb==a. b can never be lower than a as it violates our first rule that a is the smallest.
We figure out the difference from the middle and the smallest side. That's how many possible solutions we have for the other 2 sides.
Add everything together for every a and that's our solution.
The floor, ceil and % are fixes for when a is odd, the middle is .5, or +1 in case b+c is even, cause b==c is then possible.
Code:
import math
n = int(input("Enter a number: "))
total = 0
# a is the shortest side
for a in range(1, (n//3)+1):
length_left = n-a
middle_number = length_left/2
# Shortest potential side b where the distance between b and c is smaller than a (c-b < a)
b = middle_number-(math.ceil(a/2)-1)-((length_left % 2)/2)
# We calculate how far it is from the middle
max_distance_from_middle = middle_number - max(b, a)
# Add another 1 if the length is even, in case b==c
adding = math.floor(max_distance_from_middle) + (1 if length_left % 2 == 0 else 0)
total += adding
print(total)
Or in an ugly one-liner:
n = int(input("Enter a number: "))
print(sum(math.floor((n-a)/2 - max((n-a)/2 - math.ceil(a/2) + 1 - (((n-a) % 2)/2), a)) + 1 - ((n-a) % 2) for a in range(1, (n//3)+1)))
Alcuin's sequence expansion: O(1)
Alcuin's sequence [See: https://en.wikipedia.org/wiki/Alcuin%27s_sequence] is a series expansion of the polynomial below, where the nth coefficient corresponds to the nth answer, that is, the maximum amount of unique integer triangles with perimeter n.
The algorithmic implementation of this is simply a formula. The Online Encyclopaedia of Integer Sequences (OEIS) provides many formulas that achieve this, the simplest of which is:
round(n^2 / 48) (Even)
round((n+3)^2 / 48) (Odd)
[See: https://oeis.org/A005044]
This evidently has a constant time complexity, given that the only functions required are modulo 2, integer squared and round, each of which are constant time (under certain definitions).
Implementation
Expanded:
def triangles(n):
if n % 2 == 0:
return round(n ** 2 / 48)
else:
return round((n + 3) ** 2 / 48)
1-Liner:
def triangles(n): return round(n ** 2 / 48) if n%2==0 else round((n + 3) ** 2 / 48)
Or even:
def triangles(n): return round((n + 3 * n%2) ** 2 / 48)
Extra
No imports are needed.
As the OP questioned, why do we divide by 48? While I can't answer that explicitly, let's get an intuitive understanding. We are squaring numbers, so it is evidently going to expand greatly. By the time we get to 5, that would give 64 (8^2). So, there must be a constant (albeit a reciprocal) to restrict the growth of the parabola, thus the / 48.
When we graph the OP's method, it gives an alternating parabola. This explains why there is a back-and-forth with the +3 and +0.
https://mathworld.wolfram.com/AlcuinsSequence.html
import math
n = int(input())
print(round(n ** 2 / 48)) if n % 2 == 0 else print(round((n + 3)** 2 / 48))

Is ther any other way to get sum 1 to 100 with recursion?

I'm studing recursive function and i faced question of
"Print sum of 1 to n with no 'for' or 'while' "
ex ) n = 10
answer =
55
n = 100
answer = 5050
so i coded
import sys
sys.setrecursionlimit(1000000)
sum = 0
def count(n):
global sum
sum += n
if n!=0:
count(n-1)
count(n = int(input()))
print(sum)
I know it's not good way to get right answer, but there was a solution
n=int(input())
def f(x) :
if x==1 :
return 1
else :
return ((x+1)//2)*((x+1)//2)+f(x//2)*2
print(f(n))
and it works super well , but i really don't know how can human think that logic and i have no idea how it works.
Can you guys explain how does it works?
Even if i'm looking that formula but i don't know why he(or she) used like that
And i wonder there is another solution too (I think it's reall important to me)
I'm really noob of python and code so i need you guys help, thank you for watching this
Here is a recursive solution.
def rsum(n):
if n == 1: # BASE CASE
return 1
else: # RECURSIVE CASE
return n + rsum(n-1)
You can also use range and sum to do so.
n = 100
sum_1_to_n = sum(range(n+1))
you can try this:
def f(n):
if n == 1:
return 1
return n + f(n - 1)
print(f(10))
this function basically goes from n to 1 and each time it adds the current n, in the end, it returns the sum of n + n - 1 + ... + 1
In order to get at a recursive solution, you have to (re)define your problems in terms of finding the answer based on the result of a smaller version of the same problem.
In this case you can think of the result sumUpTo(n) as adding n to the result of sumUpTo(n-1). In other words: sumUpTo(n) = n + sumUpTo(n-1).
This only leaves the problem of finding a value of n for which you know the answer without relying on your sumUpTo function. For example sumUpTo(0) = 0. That is called your base condition.
Translating this to Python code, you get:
def sumUpTo(n): return 0 if n==0 else n + sumUpTo(n-1)
Recursive solutions are often very elegant but require a different way of approaching problems. All recursive solutions can be converted to non-recursive (aka iterative) and are generally slower than their iterative counterpart.
The second solution is based on the formula ∑1..n = n*(n+1)/2. To understand this formula, take a number (let's say 7) and pair up the sequence up to that number in increasing order with the same sequence in decreasing order, then add up each pair:
1 2 3 4 5 6 7 = 28
7 6 5 4 3 2 1  = 28
-- -- -- -- -- -- -- --
8 8 8 8 8 8 8 = 56
Every pair will add up to n+1 (8 in this case) and you have n (7) of those pairs. If you add them all up you get n*(n+1) = 56 which correspond to adding the sequence twice. So the sum of the sequence is half of that total n*(n+1)/2 = 28.
The recursion in the second solution reduces the number of iterations but is a bit artificial as it serves only to compensate for the error introduced by propagating the integer division by 2 to each term instead of doing it on the result of n*(n+1). Obviously n//2 * (n+1)//2 isn't the same as n*(n+1)//2 since one of the terms will lose its remainder before the multiplication takes place. But given that the formula to obtain the result mathematically is part of the solution doing more than 1 iteration is pointless.
There are 2 ways to find the answer
1. Recursion
def sum(n):
if n == 1:
return 1
if n <= 0:
return 0
else:
return n + sum(n-1)
print(sum(100))
This is a simple recursion code snippet when you try to apply the recurrent function
F_n = n + F_(n-1) to find the answer
2. Formula
Let S = 1 + 2 + 3 + ... + n
Then let's do something like this
S = 1 + 2 + 3 + ... + n
S = n + (n - 1) + (n - 2) + ... + 1
Let's combine them and we get
2S = (n + 1) + (n + 1) + ... + (n + 1) - n times
From that you get
S = ((n + 1) * n) / 2
So for n = 100, you get
S = 101 * 100 / 2 = 5050
So in python, you will get something like
sum = lambda n: ( (n + 1) * n) / 2
print(sum(100))

Find the area of an n-interesting polygon

Consider:
I am trying to find the area of an n-interesting polygon, where (n=1, A=1, n=2, A=5, n=3, A=13, n=4, A=25, and so on). So the formula for an n-interesting polygon is the area of an (n-1)-interesting polygon+(n-1)*4. When running the program, a hidden test shows that the code is wrong. What is wrong with my code?
def shapeArea(n):
if n == 0:
return 0
if n == 1:
return 1
for i in range(2, n+1):
return (shapeArea(n-1) + (n-1)*4)
As there are already coding examples, I will explain why the formula is n * n + (n-1) * (n-1)
You need to see the graph diagonally
You will notice that the sides are always n
For example, if n=4, the shape has for 4 squares on each side, thus
n * n
However, if you notice, n * n does not account for all the squares.
There are squares in between the once you accounted for
Take away the square you have accounted with n * n, you will notice
now that the side of the shape is now n-1
Thus, you take into account of the squares in between,
the formula is n * n + (n-1) * (n-1)
Example: if n = 4, the outer square is 4 * 4 = 16. Then take away the area you have just calculated, the inner squares is 3 * 3 = 9. Add together, you get 25.
I found the formula without the recursion. The test went through fine.
def shapeArea(n):
if n>=10**4 or n<1:
return False
return (n**2+(n-1)**2)
The easiest solution to the problem in JavaScript:
function shapeArea(n) {
if(n<0) {
return false
}
return (n*n) + ((n-1)*(n-1))
}
console.log(shapeArea(1))
I think the last part where you have written the 'for' loop is dodgy. If you're already using recursion why do you need a 'for' loop? Nonetheless, I did it this way without using recursion:
def shapeArea(n):
if n == 1:
return 1
return n**2 + (n-1)**2
Here's an approach one can use to derive a formula.
The shapes always have a horizontal line across the middle. If you draw a rectangle that emcompasses both the top square and the horizontal line, there will always be a void of white squares within it large enough to be partially filled by the squares below the line.
Imagine that you fill that void above the line with the squares below the line. With the exception of n=1, your shape will be changed to a rectangle that still has some white squares in it. Let's look at a few.
n=2 n=3 n=4
. X . X X . . . X . . X X X . . . . . X . . . X X X X . . .
X X X X X X . X X X . X X X X X . . X X X . . X X X X X X X
. X . . . . X X X X X X X X X X . X X X X X . X X X X X X X
. X X X . . . . . . X X X X X X X X X X X X X X
. . X . . . . . . . . X X X X X . . . . . . . .
. . X X X . . . . . . . . .
. . . X . . . . . . . . . .
The new shape can be characterized with the formula: area = height * width - gap
If we chart that out to look for patterns, it looks like this:
n | height | width | gap
1 | 1 | 1 | 0
2 | 2 | 3 | 1
3 | 3 | 5 | 2
4 | 4 | 7 | 3
Both height and gap are counting by one, and width is skip-counting by 2. You can always characterize that linear trend as n*skipValue +/- constant. In this case,
height=n
width=2n-1
gap=n-1
Plugging those terms back into our formula for the area of the gapped rectangles,
area = height * width - gap becomes area = n * (2n - 1) - (n - 1)
This may be a solution:
function shapeArea(n) {
return ((n-1) * (n*2)) + 1;
}
def shapeArea(n):
if n == 1:
return 1
square_side = n+n-1
outer_square_area = square_side**2
white_pieces = 4*(1/2)*n*(n+1)
area = outer_square - white_pieces
return area
A different approach to the problem:
If you notice, each n-interesting polygon can be contained in a surrounding square that has sides of length 2n-1. One could take this "outter square" and subtract the area of the missing white spaces.
Coming up with a formula for the white pieces is tricky because you have to add up these weird stair-step-like pieces on each of the 4 sides. The area for these weird pieces can actually be calculated using the formula for consecutive integers or 1/2*N(N+1) (For this problem N=n-1)
This can easily be seen for for the n=2, the larger surrounding square side is 2+(2-1)=3 so the total area will be 9 - 4 = 5.
To better understand how the connection to how the white area is calculated see the visualization behind the formula. Notice how counting the area of these triangular blocks is similar to adding up integers from 1...n
This is a formula to find an area of polygon for given n
def shapeArea(n):
return (n**2)+((n-1)**2)
shapeArea(3)
Output
13
Like an alternative, I found a solution using a for loop.
def shapeArea(n):
return sum([( num * 4 ) for num in range(1, n)]) + 1
This passed all the tests without performance issues:
def IncreasingSequence(sequence):
for x in range(0, len(sequence) - 1):
if sequence[y] >= sequence[x + 1]:
alt1 = sequence.copy()
alt2 = sequence.copy()
alt1.pop(x)
alt2.pop(x+1)
return (False, alt1, alt2)
return (True, sequence, sequence)
def almostIncreasingSequence(sequence):
boo, nl1, nl2 = IncreasingSequence(sequence)
if boo == False:
boo1, ll, ll1 = IncreasingSequence(nl1)
if boo1 == False:
boo2, l1, l2 =IncreasingSequence(nl2)
return boo2
return True
Use:
def shapeArea(n):
sum = 0
i = 1
while(n>1):
sum = sum + 2*i
n = n - 1
i = 2 + i
sum = sum + i
return sum
Try to calculate the sum of row wise squares (twice 2*i) and add the middle row at the end.
An easy-to-understand solution in Ruby without recursion:
def shapeArea(n)
total = 0
(1..n-1).each do |column|
total += column + (column-1)
end
(total*2) + (n+(n-1))
end
Both sides will have equal amounts of squares, except the center column that does not repeat. So we calculate the sides using the (1..n-1) loop, and the number of squares will be always column + (column - 1);
After that we just need to multiply this by 2 to get the sum of both sides (total*2) and add the center column (n+(n-1)).
This works (Python 3):
def shapeArea(n):
area = n*n + (n-1)*(n-1)
return area
print(shapeArea(5))
For C#, use this code:
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
return Convert.ToInt32(Math.Pow((2*n - 1),2) - 2 * n * (n - 1));
// Math.Pow is used to calculate a number raise to the power of some other number
If we see the given example,
when n=1, poly = 1
when n=2, poly = 5
when n=3, poly = 13
when n=4, poly = 25
the next pattern could be found by formula 2n(n-1) + 1:
def shapeArea(n):
return 2 * n * (n - 1) + 1;
In Python:
In every increasing n, the 4's table is added to the previous one's polygon number:
def solution(n):
if n == 1:
return 1
else:
c = 0
for i in range(1, n):
c += 4*i
return c + 1
I initially read the problem wrong. I thought we need to find the outside edges.
Let's first see how we can do that...and then it will lead to a more intuitive solution later for this problem.
For the number of edges, I was looking at the outermost diagonal and see 2*(n-1) edges on each of the four diagonals. It doesn't include the main four edges which are also present when n = 1.
So the total number of edges is (4 + 4 * 2 * (n-1))
Note that since we do not want to calculate number of edges, but the area, we will only use n-1 for the area rather than 2 * (n-1).
Also, we need to subtract one since we were looking at outer edges...or how many additional squares the next iteration will need...so we will use (n-1)-1
Now let’s use this to calculate the area:
n = 1 is 1
n = 2 we need to add 4 + 4* ((n-1)-1) squares or 4 + 4 * (n-2) squares
n = 3 we need to add an additional 4 + 4 * (n-2) squares
n = 4 we need to add an additional 4 + 4 * (n-2) squares
if n == 1:
return 1
area = 1
for n in range (1, n+1):
area += 4 + 4*(n-2)
return area
How can we find the formula if we don't see the partitioning trick? The definition f(1) = 1; f(n) = f(n-1) + 4*n for n > 1 relates an area to a length. We may therefore hypothesize that f(n) is quadratically related to n: a*n*n + b*b +c. We can determine the coefficients algebraically from the first 3 pairs: f(1) = 1, f(2) = 5, f(3) = 13, which give us 3 equations in 3 unknowns:
a + b + c = 1
4a + 2b + c = 5
9a + 3b + c = 13
Eliminating c = 1 - a - b first, we get
3a + b = 4
8a + 2b = 12
Eliminating b = 4 - 3a next, we get
2a = 4
Hence a = 2, b = -2, c = 1, so f(n) = 2*n*(n-1) + 1. We can verify by induction.
Note: Getting the formula for the sum of counts, f(0) = 0; f(n) = f(n-1) + n for n > 0, is even simpler because the first equation is c = 0. The remaining pair
a + b = 1
4a + 2b = 3
is easily solved to get a = b = 1/2, giving f(n) = n*(n+1)/2.
/**
Break down:
1= 1. 1 + 0
2 = 5. 1 + 4
3 = 13. 5 + 8
4 = 29 13 + 16
5 = 49 29 + 20
**/
int solution(int n) {
int area = poly(n);
System.out.println(area);
return area;
}
int poly(int n){
if(n == 1 ){
return 1;
}
else{
return poly(n-1) + side(n);
}
}
int side(int n){
if(n == 1 ){
return 0;
}
else{
return (n-1) * 4;
}
}
These worked fine for me.
n*n + (n-1) * (n-1)
n * (2*n - 1) - (n-1)
int solution(int n) {
int nSquared = n * n;
int nTimesTwo = n * 2;
int leadingCoeficient = nSquared * 2;
int area = 0;
if (n == 1) {
area = n;
} else if (n == 2){
area = 5;
} else {
area = (leadingCoeficient-nTimesTwo)+1;
}
return area;
}

How to find sum of cubes of the divisors for every number from 1 to input number x in python where x can be very large

Examples,
1.Input=4
Output=111
Explanation,
1 = 1³(divisors of 1)
2 = 1³ + 2³(divisors of 2)
3 = 1³ + 3³(divisors of 3)
4 = 1³ + 2³ + 4³(divisors of 4)
------------------------
sum = 111(output)
1.Input=5
Output=237
Explanation,
1 = 1³(divisors of 1)
2 = 1³ + 2³(divisors of 2)
3 = 1³ + 3³(divisors of 3)
4 = 1³ + 2³ + 4³(divisors of 4)
5 = 1³ + 5³(divisors of 5)
-----------------------------
sum = 237 (output)
x=int(raw_input().strip())
tot=0
for i in range(1,x+1):
for j in range(1,i+1):
if(i%j==0):
tot+=j**3
print tot
Using this code I can find the answer for small number less than one million.
But I want to find the answer for very large numbers. Is there any algorithm
for how to solve it easily for large numbers?
Offhand I don't see a slick way to make this truly efficient, but it's easy to make it a whole lot faster. If you view your examples as matrices, you're summing them a row at a time. This requires, for each i, finding all the divisors of i and summing their cubes. In all, this requires a number of operations proportional to x**2.
You can easily cut that to a number of operations proportional to x, by summing the matrix by columns instead. Given an integer j, how many integers in 1..x are divisible by j? That's easy: there are x//j multiples of j in the range, so divisor j contributes j**3 * (x // j) to the grand total.
def better(x):
return sum(j**3 * (x // j) for j in range(1, x+1))
That runs much faster, but still takes time proportional to x.
There are lower-level tricks you can play to speed that in turn by constant factors, but they still take O(x) time overall. For example, note that x // j == 1 for all j such that x // 2 < j <= x. So about half the terms in the sum can be skipped, replaced by closed-form expressions for a sum of consecutive cubes:
def sum3(x):
"""Return sum(i**3 for i in range(1, x+1))"""
return (x * (x+1) // 2)**2
def better2(x):
result = sum(j**3 * (x // j) for j in range(1, x//2 + 1))
result += sum3(x) - sum3(x//2)
return result
better2() is about twice as fast as better(), but to get faster than O(x) would require deeper insight.
Quicker
Thinking about this in spare moments, I still don't have a truly clever idea. But the last idea I gave can be carried to a logical conclusion: don't just group together divisors with only one multiple in range, but also those with two multiples in range, and three, and four, and ... That leads to better3() below, which does a number of operations roughly proportional to the square root of x:
def better3(x):
result = 0
for i in range(1, x+1):
q1 = x // i
# value i has q1 multiples in range
result += i**3 * q1
# which values have i multiples?
q2 = x // (i+1) + 1
assert x // q1 == i == x // q2
if i < q2:
result += i * (sum3(q1) - sum3(q2 - 1))
if i+1 >= q2: # this becomes true when i reaches roughly sqrt(x)
break
return result
Of course O(sqrt(x)) is an enormous improvement over the original O(x**2), but for very large arguments it's still impractical. For example better3(10**6) appears to complete instantly, but better3(10**12) takes a few seconds, and better3(10**16) is time for a coffee break ;-)
Note: I'm using Python 3. If you're using Python 2, use xrange() instead of range().
One more
better4() has the same O(sqrt(x)) time behavior as better3(), but does the summations in a different order that allows for simpler code and fewer calls to sum3(). For "large" arguments, it's about 50% faster than better3() on my box.
def better4(x):
result = 0
for i in range(1, x+1):
d = x // i
if d >= i:
# d is the largest divisor that appears `i` times, and
# all divisors less than `d` also appear at least that
# often. Account for one occurence of each.
result += sum3(d)
else:
i -= 1
lastd = x // i
# We already accounted for i occurrences of all divisors
# < lastd, and all occurrences of divisors >= lastd.
# Account for the rest.
result += sum(j**3 * (x // j - i)
for j in range(1, lastd))
break
return result
It may be possible to do better by extending the algorithm in "A Successive Approximation Algorithm for Computing the Divisor Summatory Function". That takes O(cube_root(x)) time for the possibly simpler problem of summing the number of divisors. But it's much more involved, and I don't care enough about this problem to pursue it myself ;-)
Subtlety
There's a subtlety in the math that's easy to miss, so I'll spell it out, but only as it pertains to better4().
After d = x // i, the comment claims that d is the largest divisor that appears i times. But is that true? The actual number of times d appears is x // d, which we did not compute. How do we know that x // d in fact equals i?
That's the purpose of the if d >= i: guarding that comment. After d = x // i we know that
x == d*i + r
for some integer r satisfying 0 <= r < i. That's essentially what floor division means. But since d >= i is also known (that's what the if test ensures), it must also be the case that 0 <= r < d. And that's how we know x // d is i.
This can break down when d >= i is not true, which is why a different method needs to be used then. For example, if x == 500 and i == 51, d (x // i) is 9, but it's certainly not the case that 9 is the largest divisor that appears 51 times. In fact, 9 appears 500 // 9 == 55 times. While for positive real numbers
d == x/i
if and only if
i == x/d
that's not always so for floor division. But, as above, the first does imply the second if we also know that d >= i.
Just for Fun
better5() rewrites better4() for about another 10% speed gain. The real pedagogical point is to show that it's easy to compute all the loop limits in advance. Part of the point of the odd code structure above is that it magically returns 0 for a 0 input without needing to test for that. better5() gives up on that:
def isqrt(n):
"Return floor(sqrt(n)) for int n > 0."
g = 1 << ((n.bit_length() + 1) >> 1)
d = n // g
while d < g:
g = (d + g) >> 1
d = n // g
return g
def better5(x):
assert x > 0
u = isqrt(x)
v = x // u
return (sum(map(sum3, (x // d for d in range(1, u+1)))) +
sum(x // i * i**3 for i in range(1, v)) -
u * sum3(v-1))
def sum_divisors(n):
sum = 0
i = 0
for i in range (1, n) :
if n % i == 0 and n != 0 :
sum = sum + i
# Return the sum of all divisors of n, not including n
return sum
print(sum_divisors(0))
# 0
print(sum_divisors(3)) # Should sum of 1
# 1
print(sum_divisors(36)) # Should sum of 1+2+3+4+6+9+12+18
# 55
print(sum_divisors(102)) # Should be sum of 2+3+6+17+34+51
# 114

Categories