I am currently working on the peuler question. I think I have the correct code since I tested it with the one that was provided in the example. However, when I try to run it to find the first triangular number with over 500 factors, it stays running for over 15 minutes. But when I try and find the first triangular number with over 100 factors, it finds it in under a minute.
Please see below:
My question is how can I get this too calculate quicker? Because it seems to be stuck?
#Project 12 #http://projecteuler.net/problem=12
def triangle(x) #finds the (x)st triangular number
x=(1..x)
return x.inject(:+)
end
def factors(x) #calculates how many factors (x) has
factors =[]
range=(1..x)
range.each {|num|
if x%num==0
factors << num
end
}
return factors.length
end
def project12(x) #finds the first triangular number that has over (x) factors
i=1
until factors(triangle(i)) > x
i += 1
end
return triangle(i)
end
print project12(500)
So, in triangle(x), you do x-1 additions. You run through this at i and up to i in your code, so we have (i-1) + (1 + 2 + 3 + 4 + 5 + 6 + ... + i - 1) which approximates to i^2/2. Then, in factors your code runs essentially at x time. You do this for every triangle(i), so we have 1*triangle(1) + 2*triangle(2) + 3*triangle(3) + 4*triangle(4) + ... + i*triangle(i) = 1*0 + 2*1 + 3*2 + 4*3 + ... + i*(i-1), which is approximately i^3/3 - i/3.
What does this mean? It means that based on my sketch your program runs at approximately i^3/3 - i/3 + (i-1) iterations. This is cubic time and definitely does not scale.
If, for example we had to do this up until i = 50, this would run 41699 times through. Now, let us imagine doing it just one time more: 44255 times if i = 51. That's definitely not going to scale.
Related
So what I want to do is breaking down numbers that are dozens of thousands big into smaller numbers, preferably 2~9.
The first thing came to my mind was prime factorization, for instance the number 49392 can be expressed as (2 x 2 x 2 x 2 x 3 x 3 x 7 x 7 x 7). But there are prime numbers and numbers such as 25378 = 2 × 12689 that cant be expressed with only multiplication.
So I want to break these numbers down using multiplication and addition, for example, the number 25378 could be expressed as 25346 + 32 = (2 × 19 × 23 × 29) + (2^5). Still, 23 and 29 are too big but I just picked random number just to show what I mean by using addtion and multiplication together to express big numbers, I'm sure there's a better combination of number that express 25378 than 25346 and 32.
Anyways, I thought programming this would involve ton of unnecessary if statement and would be incredibly slow in the big picture. So I was wondering, if there is a mathematical algorithm or function that does this thing? If not, I could just optimize the code myself, but I was just curious, I couldn't find anything on google myself though.
Assuming the problem is to write a number as the simplest expression containing the numbers 1-9, addition and multiplication (simplest = smallest number of operators), then this Python program does this in O(N^2) time.
A number N can be written as the sum or product of two smaller numbers, so if you've precalculated the simplest way of constructing the numbers 1..N-1, then you can find the simplest way of constructing N in O(N) time. Then it's just a matter of avoiding duplicate work -- for example without loss of generality in the expressions A+B and AB, A<=B, and nicely printing out the final expression.
def nice_exp(x, pri):
if isinstance(x, int):
return str(x)
else:
oppri = 1 if x[0] == '*' else 0
if oppri < pri:
bracks = '()'
else:
bracks = ['', '']
return '%s%s %s %s%s' % (bracks[0], nice_exp(x[1], oppri), x[0], nice_exp(x[2], oppri), bracks[1])
def solve(N):
infinity = 1e12
size = [infinity] * (N+1)
expr = [None] * (N+1)
for i in range(N+1):
if i < 10:
size[i] = 1
expr[i] = i
continue
for j in range(2, i):
if j * j > i: break
if i%j == 0 and size[j] + size[i//j] + 1 < size[i]:
size[i] = size[j] + size[i//j] + 1
expr[i] = ('*', expr[j], expr[i//j])
for j in range(1, i):
if j > i-j: break
if size[j] + size[i-j] + 1 < size[i]:
size[i] = size[j] + size[i-j] + 1
expr[i] = ('+', expr[j], expr[i-j])
return nice_exp(expr[N], 0)
print(solve(25378))
Output:
2 * (5 + 4 * 7 * (5 + 7 * 8 * 8))
While trying to solve the "paths on a grid" problem, I have written the code
def paths(n, k)
p = (1..n+k).to_a
p.combination(n).to_a.size
end
The code works fine, for instance if n == 8 and k == 2 the code returns 45 which is the correct number of paths.
However the code is very slow when using larger numbers and I'm struggling to figure out how to quicken the process.
Rather than building the array of combinations just to count it, just write the function that defines the number of combinations. I'm sure there are also gems that include this and many other combinatorics functions.
Note that I am using the gem Distribution for the Math.factorial method, but that is another easy one to write. Given that, though, I'd suggest taking #stefan's answer, as it's less overhead.
def n_choose_k(n, k)
Math.factorial(n) / (Math.factorial(k) * Math.factorial(n - k))
end
n_choose_k(10, 8)
# => 45
Note that the n and k here refer to slightly different things than in your method, but I am keeping them as it is highly standard nomenclature in combinatorics for this function.
def combinations(n, k)
return 1 if k == 0 or k == n
(k + 1 .. n).reduce(:*) / (1 .. n - k).reduce(:*)
end
combinations(8, 2) #=> 28
Explanation about the math part
The original equation is
combinations(n, k) = n! / k!(n - k)!
Since n! / k! = (1 * 2 * ... * n) / (1 * 2 * ... * k), for any k <= n there is a (1 * 2 * ... * k) factor both in the numerator and in the denominator, so we can cancel this factor. This makes the equation become
combinations(n, k) = (k + 1) * (k + 2) * ... * (n) / (n - k)!
which is exactly what I did in my Ruby code.
The answers that suggest computing full factorials will generate lots of unnecessary overhead when working with big numbers. You should use the method below for calculating the binomial coefficient: n!/(k!(n-k)!)
def n_choose_k(n, k)
return 0 if k > n
result = 1
1.upto(k) do |d|
result *= n
result /= d
n -= 1
end
result
end
This will perform the minimum operations needed. Note that incrementing d while decrementing n guarantees that there will be no rounding errors. For example, {n, n+1} is guaranteed to have at least one element divisible by two, {n, n+1, n+2} is guaranteed to have at least one element divisible by three and so on.
Your code can be rewritten as:
def paths(x, y)
# Choice of x or y for the second parameter is arbitrary
n_choose_k(x + y, x)
end
puts paths(8, 2) # 45
puts paths(2, 8) # 45
I assume that n and k in the original version were meant to be dimensions so i labeled them x and y instead. There's no need to generate an array here.
Edit: Here is a benchmark script...
require 'distribution'
def puts_time
$stderr.puts 'Completed in %f seconds' % (Time.now - $start_time)
$start_time = Time.now
end
def n_choose_k(n, k)
return 0 if k > n
result = 1
1.upto(k) do |d|
result *= n
result /= d
n -= 1
end
result
end
def n_choose_k_distribution(n, k)
Math.factorial(n) / (Math.factorial(k) * Math.factorial(n - k))
end
def n_choose_k_inject(n, k)
(1..n).inject(:*) / ((1..k).inject(:*) * (1..n-k).inject(:*))
end
def benchmark(&callback)
100.upto(300) do |n|
25.upto(75) do |k|
callback.call(n, k)
end
end
end
$start_time = Time.now
puts 'Distribution gem...'
benchmark { |n, k| n_choose_k_distribution(n, k) }
puts_time
puts 'Inject method...'
benchmark { |n, k| n_choose_k_inject(n, k) }
puts_time
puts 'Answer...'
benchmark { |n, k| n_choose_k(n, k) }
puts_time
Output on my system is:
Distribution gem...
Completed in 1.141804 seconds
Inject method...
Completed in 1.106018 seconds
Answer...
Completed in 0.150989 seconds
Since you're interested in the count rather than the actual combination sets, you should do this with a choose function. The mathematical definition involves evaluating three different factorials, but there's a lot of cancellation going on so you can speed it up by using ranges to avoid the calculations that will be cancelled anyway.
class Integer
def choose(k)
fail 'k > n' if k > self
fail 'args must be positive' if k < 0 or self < 1
return 1 if k == n || k == 0
mm = [self - k, k].minmax
(mm[1]+1..self).reduce(:*) / (2..mm[0]).reduce(:*)
end
end
p 8.choose 6 # => 28
To solve your paths problem, you could then define
def paths(n, k)
(n + k).choose(k)
end
p paths(8, 2) # => 45
The reduce/inject versions are nice. But since speed seemed to be a bit of an issue, I'd suggest the n_choose_k versions from #google-fail.
It is quite insightful and suggests a ~10-fold speed increase.
I would suggest that the iteration use the Lesser of k and ( n - k ).
N-choose-K and N-choose-(N-K) produce the same result (the factors in the denominator are simply reversed). So something like a 52-choose-51 could be done in one iteration.
I usually do the following:
class Integer
def !
(2..self).reduce(1, :*)
end
def choose(k)
self.! / (k.! * (self-k).!)
end
end
Benchmarking:
k = 5
Benchmark.bm do |x|
[10, 100, 1000, 10000, 100000].each do |n|
x.report("#{n}") { n.choose(k) }
end
end
On my machine I get:
user system total real
10 0.000008 0.000001 0.000009 ( 0.000006)
100 0.000027 0.000003 0.000030 ( 0.000031)
1000 0.000798 0.000094 0.000892 ( 0.000893)
10000 0.045911 0.013201 0.059112 ( 0.059260)
100000 4.885310 0.229735 5.115045 ( 5.119902)
Not the fastest thing on the planet, but it's okay for my uses. If it ever becomes a problem, then I can think about optimizing
I've been staring at this problem for hours and I'm still as lost as I was at the beginning. It's been a while since I took discrete math or statistics so I tried watching some videos on youtube, but I couldn't find anything that would help me solve the problem in less than what seems to be exponential time. Any tips on how to approach the problem below would be very much appreciated!
A certain species of fern thrives in lush rainy regions, where it typically rains almost every day.
However, a drought is expected over the next n days, and a team of botanists is concerned about
the survival of the species through the drought. Specifically, the team is convinced of the following
hypothesis: the fern population will survive if and only if it rains on at least n/2 days during the
n-day drought. In other words, for the species to survive there must be at least as many rainy days
as non-rainy days.
Local weather experts predict that the probability that it rains on a day i ∈ {1, . . . , n} is
pi ∈ [0, 1], and that these n random events are independent. Assuming both the botanists and
weather experts are correct, show how to compute the probability that the ferns survive the drought.
Your algorithm should run in time O(n2).
Have an (n + 1)×n matrix such that C[i][j] denotes the probability that after ith day there will have been j rainy days (i runs from 1 to n, j runs from 0 to n). Initialize:
C[1][0] = 1 - p[1]
C[1][1] = p[1]
C[1][j] = 0 for j > 1
Now loop over the days and set the values of the matrix like this:
C[i][0] = (1 - p[i]) * C[i-1][0]
C[i][j] = (1 - p[i]) * C[i-1][j] + p[i] * C[i - 1][j - 1] for j > 0
Finally, sum the values from C[n][n/2] to C[n][n] to get the probability of fern survival.
Dynamic programming problems can be solved in a top down or bottom up fashion.
You've already had the bottom up version described. To do the top-down version, write a recursive function, then add a caching layer so you don't recompute any results that you already computed. In pseudo-code:
cache = {}
function whatever(args)
if args not in cache
compute result
cache[args] = result
return cache[args]
This process is called "memoization" and many languages have ways of automatically memoizing things.
Here is a Python implementation of this specific example:
def prob_survival(daily_probabilities):
days = len(daily_probabilities)
days_needed = days / 2
# An inner function to do the calculation.
cached_odds = {}
def prob_survival(day, rained):
if days_needed <= rained:
return 1.0
elif days <= day:
return 0.0
elif (day, rained) not in cached_odds:
p = daily_probabilities[day]
p_a = p * prob_survival(day+1, rained+1)
p_b = (1- p) * prob_survival(day+1, rained)
cached_odds[(day, rained)] = p_a + p_b
return cached_odds[(day, rained)]
return prob_survival(0, 0)
And then you would call it as follows:
print(prob_survival([0.2, 0.4, 0.6, 0.8])
Working on Problem 12 of Project Euler:
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
Let us list the factors of the first seven triangle numbers:
1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
We can see that 28 is the first triangle number to have over five divisors.
What is the value of the first triangle number to have over five hundred divisors?
Here's what I've got:
require 'reusable'
# The idea here is that 2^n is the smallest number with n factors,
# according to their definition, so it's a good place to start.
# It also happens to be a HUGE number, so I'm worried I'm thinking
# about this wrong. Did 4999 instead of 5000, just to make sure
# I didn't overshoot.
start = 2 * 4999
# The faster way to calculate the nth Triangle number
def nthTriangle(n)
n * (n + 1) / 2
end
def answer(num)
i = startingTriangle(num)
while true
triangle = i*(i+1)/2
puts triangle
factors = numFactors(triangle)
return "#{triangle} is triangle number #{i}, with #{factors} factors." if factors > num
i += 1
end
end
# Basic reversal of the nthTriangle thing to figure
# out which n to start with in the answer function.
def startingTriangle(n)
power = n - 2
sqrt(power * 2).to_i - 1
end
puts answer(5000)
And that required file (where I'm trying to put methods I'll reuse in a bunch of Euler problems):
def primesUpTo(n)
nums = [0, 0] + (2..n).to_a
(2..sqrt(n).to_i+1).each do |i|
if nums[i].nonzero?
(i**2..n).step(i) {|m| nums[m] = 0}
end
end
nums.find_all {|m| m.nonzero?}
end
def prime?(n)
test = primesUpTo(sqrt(n).to_i)
test.each do |i|
if n % i == 0
return false
end
end
true
end
# Just for faster, more intuitive (to me) array summing
def sum(array)
array.inject(0) {|s, n| s + n }
end
# Ditto
def product(array)
array.inject(1) {|p, n| p * n}
end
# I don't like typing the 'Math.'
def sqrt(n)
Math.sqrt(n)
end
# Returns an array of arrays of the prime factors of num
# Form [[factor1, power1],[factor2, power2]]
# Ex: primeFactors(12) == [[2,2],[3,1]]
def primeFactors(n)
array = []
# 2 3
primesUpTo((n/2).to_i+1).select{ |i| n % i == 0 }.each do |p|
pcount = 1
n = n / p
while n % p == 0
pcount += 1
n = n / p
end
array << [p, pcount]
end
array
end
# Returns the number of factors a number has
# INCLUDING both the number itself and 1
# ex: numFactors(28) = 6
def numFactors(n)
return 2 if prime?(n)
product = 1
primeFactors(n).each do |i|
product *= i[1] + 1
end
product
end
My problem is that my code is really super slow. If I start at 1 instead of my start number, it takes a minute + before it gets to like 200000 (nowhere near 2^4999). But apart from scrapping the library prime-number solution and adding all primes to an array I keep referring to -- which I feel would only make it a small amount faster -- I can't think of how to make this much faster. And it needs to be WAY faster.
Am I thinking about this all wrong? Any suggestions?
Also useful would be any suggestions for how to improve the efficiency of any of my library methods, which I'll probably be using again and again. I wanted to make them from scratch so I understood them, but I'm afraid they're very inefficient.
From your code:
The idea here is that 2^n is the smallest number with n factors
From the stated Project Euler task:
We can see that 28 is the first triangle number to have over five divisors.
I'm not sure why you think 2^n is the smallest number with n factors, but the example given in the question clearly proves your assumption wrong, as 2^5 = 32, which is greater than 28.
My solution starts the search at 1 and is reasonably efficient. I don't use primes at all.
Addendum: For the sake of completeness, the other large issue besides starting at a number far too high is searching for greater than 5000 divisors rather than greater than 500, as you noticed and pointed out in the comments.
Consider the problem in which you have a value of N and you need to calculate how many ways you can sum up to N dollars using [1,2,5,10,20,50,100] Dollar bills.
Consider the classic DP solution:
C = [1,2,5,10,20,50,100]
def comb(p):
if p==0:
return 1
c = 0
for x in C:
if x <= p:
c += comb(p-x)
return c
It does not take into effect the order of the summed parts. For example, comb(4) will yield 5 results: [1,1,1,1],[2,1,1],[1,2,1],[1,1,2],[2,2] whereas there are actually 3 results ([2,1,1],[1,2,1],[1,1,2] are all the same).
What is the DP idiom for calculating this problem? (non-elegant solutions such as generating all possible solutions and removing duplicates are not welcome)
Not sure about any DP idioms, but you could try using Generating Functions.
What we need to find is the coefficient of x^N in
(1 + x + x^2 + ...)(1+x^5 + x^10 + ...)(1+x^10 + x^20 + ...)...(1+x^100 + x^200 + ...)
(number of times 1 appears*1 + number of times 5 appears * 5 + ... )
Which is same as the reciprocal of
(1-x)(1-x^5)(1-x^10)(1-x^20)(1-x^50)(1-x^100).
You can now factorize each in terms of products of roots of unity, split the reciprocal in terms of Partial Fractions (which is a one time step) and find the coefficient of x^N in each (which will be of the form Polynomial/(x-w)) and add them up.
You could do some DP in calculating the roots of unity.
You should not go from begining each time, but at max from were you came from at each depth.
That mean that you have to pass two parameters, start and remaining total.
C = [1,5,10,20,50,100]
def comb(p,start=0):
if p==0:
return 1
c = 0
for i,x in enumerate(C[start:]):
if x <= p:
c += comb(p-x,i+start)
return c
or equivalent (it might be more readable)
C = [1,5,10,20,50,100]
def comb(p,start=0):
if p==0:
return 1
c = 0
for i in range(start,len(C)):
x=C[i]
if x <= p:
c += comb(p-x,i)
return c
Terminology: What you are looking for is the "integer partitions"
into prescibed parts (you should replace "combinations" in the title).
Ignoring the "dynamic programming" part of the question, a routine
for your problem is given in the first section of chapter 16
("Integer partitions", p.339ff) of the fxtbook, online at
http://www.jjj.de/fxt/#fxtbook