Project Euler #23 [closed] - c#

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 6 years ago.
Improve this question
I'm currently trying to solve the project euler problem number 23 :
A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
However my code is not giving correct result while it seems to be completely fine. Am I calculating more than the enough numbers ?
private static void Main()
{
List<int> AbudantNumbers = new List<int>();
long sum = 0;
for (int i = 12; i <= 28123; i++)
{
int abudantNumber = GetProperDivisor(i);
if (abudantNumber > i)
{
AbudantNumbers.Add(i);
}
}
for (int k = 1; k <= 28123; k++)
{
int count = 0;
for (int i = 0; i < AbudantNumbers.Count; i++)
{
count = 0;
if (AbudantNumbers[i] > k)
{
break;
}
for (int j = i; j < AbudantNumbers.Count; j++)
{
if (AbudantNumbers[j] > k)
{
break;
}
if (AbudantNumbers[i] + AbudantNumbers[j] == k)
{
count++;
break;
}
}
}
if (count == 0)
{
sum += k;
}
}
Console.WriteLine(sum);
Console.ReadKey();
}
private static int GetProperDivisor(int input)
{
int sum = 1;
for (int i = 2; i <= input / 2; i++)
{
if (input%i == 0)
{
sum += i;
}
}
return sum;
}
My result is : 297632990
Correct result is : 4179871
Quite big difference when there are no obvious mistakes in my code.
My second approach :
for (int k = 1; k <= 28123; k++)
{
var k1 = k;
int count =
(from t1 in AbudantNumbers.TakeWhile(t1 => t1 <= k1) let a = t1 select t1).Count(
t1 => AbudantNumbers.TakeWhile(t => t <= k).Any(t => t1 + t == k));
if (count == 0)
{
sum += k;
}
}
My idea is to get all the abundant numbers lesser than 28123 than check all the integers lesser than 28123 (everything above have a sum of 2 abundant numbers) than rotate all the abundant numbers and lastly check if abundantNumber1 + abundantNumber2 == currentNumber if so we break out of the loop because we need only the ones that don't have a sum of 2 abundant numbers.

When you find that the current number k is the sum of two abundant numbers a[i] + a[j], you set the count and then break out of the loop – out of the inner loop over j, that is.
That means that you still consider all remaining is, for which you reset the count. Effectively, you consider only the last abundant number that is smaller or equal to k. It is not likely that the sum of the two numbers involves this number, so your condition for adding the numbers, count == 0 will be wrong in most cases.
You must break out of the two innermost loops when you find out that k == a[i] + a[j]. C# doesn't seem to have labelled loops, so it is probably best to refactor the the inner loops into a function that takes the list of abundant numbers and k as arguments; you can then simply return true when your condition is met.
As a quick but ugly fix, you can add
if (count) break;
after the loop over j.
Your approach is very ineffective, though. It might be better to write only two nested loops over all abundant numbers and then add their sums to a set when they are smaller than 28124. Then add all numbers from 1 to 28124 that are not in the set.

Related

Find the number whose digits have the highest sum in a given range [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
For a given input n, the task is to find the largest integer that is <= n and has the highest digit sum.
For example:
solve(100) = 99. Digit Sum for 99 = 9 + 9 = 18. No other number <= 100 has a higher digit sum.
solve(10) = 9
solve(48) = 48. Note that 39 is also an option, but 48 is larger.
Input range is 0 < n < 1e11
What have I tried?
I tried 2 methods. Firstly, I tried getting each digit with Math operations like this:
public static long solve(long n)
{
var answer = 0;
var highestSum = 0;
for (var i = 1; i <= n; i++)
{
var temp = i;
var sum = 0;
while (temp > 0)
{
sum += temp % 10;
temp /= 10;
}
if (sum >= highestSum)
{
highestSum = sum;
answer = i;
}
}
return answer;
}
My second try, I tried using Linq extensions, like this:
public static long solve(long n)
{
var answer = 0;
var highestSum = 0;
for (var i = 1; i <= n; i++)
{
var sum = i.ToString().Sum(x => x - '0');
if (sum >= highestSum)
{
highestSum = sum;
answer = i;
}
}
return answer;
}
Both of my solutions seem to return the correct value and work for smaller values, but for larger input, they seem to take a very long time to execute. How to make it run through numbers faster? Is there a specific algorithm for this task, or am I doing something else wrong?
We can achieve this O(number of digits in n)
We can achieve this if we iteratively reduce a digit and change all other digits on its right to 9.
Let n be our current number.
We can find next number using the below :
b is a power of 10 to represent position of current digit. After every iteration we reduce n to n/10 and change b to b*10.
We use (n – 1) * b + (b – 1);
For eg, if the number is n = 521 and b = 1, then
(521 – 1) * 1 + (1-1) which gives you 520, which is the thing we need to do, reduce the position number by 1 and replace all other numbers to the right by 9.
After n /= 10 gives you n as 52 and b*=10 gives you b as 10, which is again executed as (52-1)*(10) + 9 which gives you 519, which is what we have to do, reduce the current index by 1 and increase all other rights by 9.
static int findMax(int x)
{
int b = 1, ans = x;
while (x!=0)
{
int cur = (x - 1) * b + (b - 1);
if (sumOfDigits(cur) >= sumOfDigits(ans) && cur > ans))
ans = cur;
x /= 10;
b *= 10;
}
return ans;
}
int sumOfDigits(int a)
{
int sum = 0;
while (a)
{
sum += a % 10;
a /= 10;
}
return sum;
}
The accepted answer is brilliant, but I was dead-set on figuring out a way to determine the correct answer without actually summing the digits and comparing the sums to each other.
I tried a few things (as you can see if you look at the edit history), but I couldn't find the formula. In desperation, I wrote a utility to show me all the numbers from 1 to 9999999 that did not have a smaller number with a larger sum to see what pattern I was missing by not looking on a large enough scale.
I was somewhat surprised that only 253 numbers out of the first 10 million have the largest sum compared to their lessers! Somehow I thought that number would be bigger.
Also, it turns out that there is an obvious pattern that appears fairly quickly, and it remained constant for 10 million iterations, so I think it's a good one.
Here's a small sample of some blocks of consecutive output:
0,1,2,3,4,5,6,7,8,9,
18,19,28,29,38,39,48,49,
58,59,68,69,78,79,88,89,98,99,189,198
8899,8989,8998,8999,
9899,9989,9998,9999,
18999,19899,19989,19998,19999
98999,99899,99989,99998,99999,
189999,198999,199899,199989,199998,199999
7899999,7989999,7998999,7999899,7999989,7999998,7999999,
8899999,8989999,8998999,8999899,8999989,8999998,8999999,
9899999,9989999,9998999,9999899,9999989,9999998,9999999
It's so obviously clear!
If the number is one digit, then it's the highest.
If all but the first digit are either all 9's or all 9's with a single 8, then it's sum is the highest.
Otherwise the highest number is the one whose first digit is one less than the original, followed by all 9's.
Here's a code implementation:
public static long Solve(long n)
{
if (HasValidSuffix(n)) return n;
long firstDigit;
int numDigits;
// Loop to determine the first digit and number of digits in the input
for (firstDigit = n, numDigits = 1; firstDigit > 9; firstDigit /= 10, numDigits++) ;
return Enumerable.Range(0, numDigits - 1)
.Aggregate(firstDigit - 1, (accumulator, next) => accumulator * 10 + 9);
}
// Returns true for positive numbers less than 10 or
// numbers that end in either all 9's or all 9's and one 8
public static bool HasValidSuffix(long input)
{
var foundAnEight = false;
for (var n = input; n > 9; n /= 10)
{
var lastDigit = n % 10;
if (lastDigit < 8) return false;
if (lastDigit == 9) continue;
if (foundAnEight) return false;
foundAnEight = true;
}
return true;
}

how many numbers between 1 to 10 billion contains 14

i tried this code but it takes so long and I can not get the result
public long getCounter([FromBody]object req)
{
JObject param = Utility.GetRequestParameter(req);
long input = long.Parse(param["input"].ToString());
long counter = 0;
for (long i = 14; i <= input; i++)
{
string s = i.ToString();
if (s.Contains("14"))
{
counter += 1;
}
}
return counter;
}
please help
We can examine all non-negative numbers < 10^10. Every such number can be represented with the sequence of 10 digits (with leading zeroes allowed).
How many numbers include 14
Dynamic programming solution. Let's find the number of sequences of a specific length that ends with the specific digit and contains (or not) subsequence 14:
F(len, digit, 0) is the number of sequences of length len that ends with digit and do not contain 14, F(len, digit, 1) is the number of such sequences that contain 14. Initially F(0, 0, 0) = 1. The result is the sum of all F(10, digit, 1).
C++ code to play with: https://ideone.com/2aS17v. The answer seems to be 872348501.
How many times the numbers include 14
First, let's place 14 at the end of the sequence:
????????14
Every '?' can be replaced with any digit from 0 to 9. Thus, there are 10^8 numbers in the interval that contains 14 at the end. Then consider ???????14?, ??????14??, ..., 14???????? numbers. There are 9 possible locations of 14 sequence. The answer is 10^8 * 9 = 90000000.
[Added by Matthew Watson]
Here's the C# version of the C++ implementation; it runs in less than 100ms:
using System;
namespace Demo
{
public static class Program
{
public static void Main(string[] args)
{
const int M = 10;
int[,,] f = new int [M + 1, 10, 2];
f[0, 0, 0] = 1;
for (int len = 1; len <= M; ++len)
{
for (int d = 0; d <= 9; ++d)
{
for (int j = 0; j <= 9; ++j)
{
f[len,d,0] += f[len - 1,j,0];
f[len,d,1] += f[len - 1,j,1];
}
}
f[len,4,0] -= f[len - 1,1,0];
f[len,4,1] += f[len - 1,1,0];
}
int sum = 0;
for (int i = 0; i <= 9; ++i)
sum += f[M,i,1];
Console.WriteLine(sum); // 872,348,501
}
}
}
If you want a brute force solution it could be something like this (please, notice, that we should avoid time consuming string operations like ToString, Contains):
int count = 0;
// Let's use all CPU's cores: Parallel.For
Parallel.For(0L, 10000000000L, (v) => {
for (long x = v; x > 10; x /= 10) {
// Get rid of ToString and Contains here
if (x % 100 == 14) {
Interlocked.Increment(ref count); // We want an atomic (thread safe) operation
break;
}
}
});
Console.Write(count);
It returns 872348501 within 6 min (Core i7 with 4 cores at 3.2GHz)
UPDATE
My parallel code calculated the result as 872,348,501 in 9 minutes on my 8- processor-core Intel Core I7 PC.
(There is a much better solution above that takes less than 100ms, but I shall leave this answer here since it provides corroborating evidence for the fast answer.)
You can use multiple threads (one per processor core) to reduce the calculation time.
At first I thought that I could use AsParallel() to speed this up - however, it turns out that you can't use AsParallel() on sequences with more than 2^31 items.
(For completeness I'm including my faulty implementation using AsParallel at the end of this answer).
Instead, I've written some custom code to break the problem down into a number of chunks equal to the number of processors:
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static void Main()
{
int numProcessors = Environment.ProcessorCount;
Task<long>[] results = new Task<long>[numProcessors];
long count = 10000000000;
long elementsPerProcessor = count / numProcessors;
for (int i = 0; i < numProcessors; ++i)
{
long end;
long start = i * elementsPerProcessor;
if (i != (numProcessors - 1))
end = start + elementsPerProcessor;
else // Last thread - go right up to the last element.
end = count;
results[i] = Task.Run(() => processElements(start, end));
}
long sum = results.Select(r => r.Result).Sum();
Console.WriteLine(sum);
}
static long processElements(long inclusiveStart, long exclusiveEnd)
{
long total = 0;
for (long i = inclusiveStart; i < exclusiveEnd; ++i)
if (i.ToString().Contains("14"))
++total;
return total;
}
}
}
The following code does NOT work because AsParallel() doesn't work on sequences with more than 2^31 items.
static void Main(string[] args)
{
var numbersContaining14 =
from number in numbers(0, 100000000000).AsParallel()
where number.ToString().Contains("14")
select number;
Console.WriteLine(numbersContaining14.LongCount());
}
static IEnumerable<long> numbers(long first, long count)
{
for (long i = first, last = first + count; i < last; ++i)
yield return i;
}
You compute the count of numbers of a given length ending in 1, 4 or something else that don't contain 14. Then you can extend the length by 1.
Then the count of numbers that do contain 14 is the count of all numbers minus those that don't contain a 14.
private static long Count(int len) {
long e1=0, e4=0, eo=1;
long N=1;
for (int n=0; n<len; n++) {
long ne1 = e4+e1+eo, ne4 = e4+eo, neo = 8*(e1+e4+eo);
e1 = ne1; e4 = ne4; eo = neo;
N *= 10;
}
return N - e1 - e4 - eo;
}
You can reduce this code a little, noting that eo = 8*e1 except for the first iteration, and then avoiding the local variables.
private static long Count(int len) {
long e1=1, e4=1, N=10;
for (int n=1; n<len; n++) {
e4 += 8*e1;
e1 += e4;
N *= 10;
}
return N - 9*e1 - e4;
}
For both of these, Count(10) returns 872348501.
One easy way to calculate the answer is,
You can fix 14 at a place and count the combination of the remaining numbers right to it,
and do this for all the possible positions where 14 can be place such that the number is still less than 10000000000,Lets take a example,
***14*****,
Here the '*' before 14 can be filled by 900 ways and the * after 14 can be filled by 10^5 ways so total occurrence will be 10^5*(900),
Similarly you can fix 14 at other positions to calculate the result and this solution will be very fast O(10) or simply O(1), while the previous solution was O(N), where N is 10000000000
You can use the fact that in each 1000 (that is from 1 to 1000 and from 1001 to 2000 etc)
the 14 is found: 19 times so when you receive your input divide it by 1000 for example you received 1200 so 1200/1000
the result is 1 and remainder 200, so we have 1 * 19 "14"s and then you can loop over the 200.
you can extend for 10000 (that is count how many "14"s there are in 10000 and fix it to a global variable) and start dividing by 10000 then and apply the equation above, then you divide the remainder by 1000 and apply the equation and add the two results.
You can extend it as the fact that for all hundreds (that is from 1 to 100 and from 201 to 300) the "14" is found only 1 except for the second hundred (101 to 200).

When arrays go awry?

I'm trying to learn C# by solving mathematical problems. For example, I'm working on finding the sum of factors of 3 or 5 in the first 1000 positive numbers. I have the basic shell of the code laid out, but it isn't behaving how I'm expecting it to.
Right now, instead of getting a single output of 23, I am instead getting 1,1,3,3,5,5,7,7,9,9. I imagine I messed up the truncate function somehow. Its a bloody mess, but its the only way I can think of checking for factors. Second, I think that the output is writing during the loop, instead of patiently waiting for the for() loop to finish.
using System;
namespace Problem1
{
class Problem1
{
public static void Main()
{
//create a 1000 number array
int[] numberPool = new int[10];
//use for loop to assign the first 1000 positive numbers to the array
for (int i = 0; i < numberPool.Length; i++)
{
numberPool[i] = i + 1;
}
//check for factors of 3 or 5 using if/then statment
foreach (int i in numberPool)
if ((i / 3) == Math.Truncate((((decimal)(i / 3)))) || ((i / 5) == Math.Truncate(((decimal)(i / 5)))))
{
numberPool[i] = i;
}
else
{
numberPool[i] = 0;
}
//throw the 0s and factors together and get the sum!
int sum = 0;
for (int x = 0;x < numberPool.Length;x++)
{
sum = sum + numberPool[x];
}
Console.WriteLine(sum);
Console.ReadLine();
//uncomment above if running in vbs
}
}
}
The foreach loop has a few errors.
If you want to modify the array you are looping through use a for loop. Also, use modulus when checking remainders.
for (int i = 0; i < numberPool.Length; i++)
{
if (numberPool[i] % 3 == 0 || numberPool[i] % 5 == 0)
{
// Do nothing
}
else
{
numberPool[i] = 0;
}
}
Modulus (%) will give the remainder when dividing two integers.
Another useful shortcut, variable = variable + x can be replaced with variable += x
Please note that there are more concise ways of doing this but since you are learning the language I will leave that for you to find.
#kailanjian gave some great advice for you but here is another way your initial logic can be simplified for understanding:
//the sum of factors
int sum = 0;
//the maximum number we will test for
int maxNum = 1000;
//iterate from 1 to our max number
for (int i = 1; i <= maxNum; i++)
{
//the number is a factor of 3 or 5
if (i % 3 == 0 || i % 5 == 0)
{
sum += i;
}
}
//output our sum
Console.WriteLine(sum);
You also stated:
Second, I think that the output is writing during the loop, instead of patiently waiting for the for() loop to finish.
Your program logic will execute in the order that you list it and won't move on to the next given command until it is complete with the last. So your sum output will only be printed once it has completed our for loop iteration.

(Relatively) Quickly find some divisor for a number < 10 000 000

Assume for everything that I'm talking only about natural numbers less than 10 million.
I'm looking to pre-generate a list of the Lowest Prime Divisor (LPD) for all numbers under 10 000 000. For example, LPD(14) == 2, LPD(15) == 3, and the LPD of any prime is itself.
I have pre-generated all of the primes. Accessing the nth prime is a simple array lookup. With an efficiency of: O(1)
I have pre-generated a lookup table for determining if a given number is prime. Accessing the nth prime is a simple array lookup. With an efficiency of: O(1)
Now, my naive algorithm to calculate the LPD of a given number is to loop through all the primes until one prime divides the number. But this takes a really long time. I can generate all primes under 10 million in half the time it takes to find the lowest divisor for all the numbers (using the Sieve of Atkin, which I don't understand, but implemented from pseudocode).
Is there a better algorithm for calculating the Lowest Prime Divisor?
Not actually sure why you expect higher performance for much the same problem.
Rather than divide, a sieve approach would take each prime, mark all its multiples as having itself as the lowest prime factor, unless already marked.
int lpf[MAX] = {};
int primes[MAX_PRIME];
for(int i = 0; i < MAX_PRIME; ++i)
{
int mult = primes[i];
while(mult < MAX)
{
if (lpf[mult] == 0)
{
lpf[mult] = primes[i];
}
mult += primes[i];
}
}
Any unmarked number at the end is itself prime, so this approach takes the same time as finding all the primes under MAX.
As adapted from #Keith's answer, the new code runs much faster (13% the old speed!):
public void SieveDivisors() {
int iNum, iPrime, i6Prime;
_iaFirstDivisors = new int[_iLimit];
_iaFirstDivisors[1] = 1;
//Start at the largest primes, then work down. This way, we never need to check if the
// lowest prime multiple is already found, we just overwrite it
//Also, skip any multiples of 2 or 3, because setting those is a waste of time
for (int iPrimeIndex = _iaPrimes.Length - 1; iPrimeIndex >= 1; iPrimeIndex--) {
iPrime = _iaPrimes[iPrimeIndex];
i6Prime = iPrime * 6;
for (iNum = iPrime; iNum < _iLimit; iNum += i6Prime) {
_iaFirstDivisors[iNum] = iPrime;
}
for (iNum = iPrime * 5; iNum < _iLimit; iNum += i6Prime) {
_iaFirstDivisors[iNum] = iPrime;
}
}
//Then record all multiples of 2 or 3
for (iNum = 3; iNum < _iLimit; iNum += 6) {
_iaFirstDivisors[iNum] = 3;
}
for (iNum = 2; iNum < _iLimit; iNum += 2) {
_iaFirstDivisors[iNum] = 2;
}
}
You say you are using the Sieve of Atkin to produce the list of primes. If you use the Sieve of Eratosthenes you automatically get your LPD array - it is simply the array you use for sieve. Instead of storing a boolean track the first prime which makes the number composite.
Here is some pseudo C code:
int lpd[MAX] = {};
int primes[MAX_PRIMES];
int nprimes = 0;
void sieve() {
for (int p = 2; p*p < MAX; ++p) {
if (lpd[p] == 0) {
primes[nprimes++] = p;
lpd[p] = p;
for (int q = p*p; q < MAX; q += p) {
if (lpd[q] == 0) { lpd[q] = p; }
}
}
}
}
At the end the array lpd[] will contain the lowest prime divisors and primes[] will contain the list of prime numbers.

C# Triangle numbers optimization

The task is to find a triangle number which has at least 500 divisors.
For example 28 has 6 divisors: 1,2,4,7,14,28
My code works for up to 200 divisors, but for 500 it runs forever...
Is there any way to optimize the code. For instance I thought of dynamic optimization and memoization, but couldn't find a way to do it?
int sum = 0;
int counter = 0;
int count = 1;
bool isTrue = true;
while (isTrue)
{
counter = 0;
sum += count;
for (int j = 1; j <= sum; j++)
{
if (sum % j == 0)
{
counter++;
if (counter == 500)
{
isTrue = false;
Console.WriteLine("Triangle number: {0}", sum);
break;
}
}
}
count++;
}
Console.WriteLine("Number of divisors: {0}", counter);
Ignore the fact that the number is a triangle number. If you can solve this problem quickly:
given any number n, determine the number of divisors it has
then obviously you can solve Euler #12 quickly. Just list the triangle numbers, which are easy to calculate, determine the number of divisors of each, and stop when you get a result 500 or larger.
So how do you determine the number of divisors quickly? As you've discovered, when the numbers get big, it's a lot of work.
Here's a hint. Suppose you already have the prime factorization. Let's pick a number, say, 196. Factorize that into prime numbers:
196 = 2 x 2 x 7 x 7
I can tell you just by glancing at the factorization that 196 has nine divisors. How?
Because any divisor of 196 is of the form:
(1, 2 or 2x2) x (1, 7 or 7x7)
So obviously there are nine possible combinations:
1 x 1
1 x 7
1 x 7 x 7
2 x 1
2 x 7
2 x 7 x 7
2 x 2 x 1
2 x 2 x 7
2 x 2 x 7 x 7
Pick another number. 200, lets say. Thats 2 x 2 x 2 x 5 x 5. So there are twelve possibilities:
1 x 1
1 x 5
1 x 5 x 5
2 x 1
2 x 5
...
2 x 2 x 2 x 5 x 5
See the pattern? You take the prime factorization, group them by prime, and count how many are in each group. Then you add one to each of those numbers and multiply them together. Again, in 200 there are three twos and two fives in the prime factorization. Add one to each: four and three. Multiply them together: twelve. That's how many divisors there are.
So you can find the number of divisors very quickly if you know the prime factorization. We have reduced the divisor problem to a much easier problem: Can you figure out how to produce a prime factorization quickly?
here are some optimizations I'll just throw out there for you.
the easiest thing is to change
for (int j = 1; j <= sum; j++)
{
if (sum % j == 0)
{
counter++;
if (counter == 500)
{
isTrue = false;
Console.WriteLine("Triangle number: {0}", sum);
break;
}
}
}
if you've found 1 divisor, you've found 2 divisors, so change it to
for (int j = 1; j <= sum; j++)
{
if (sum % j == 0)
{
if(sum/j < j)
break;
else if(sum/j == j)
counter++;
else
counter +=2;
if (counter == 500)
{
isTrue = false;
Console.WriteLine("Triangle number: {0}", sum);
break;
}
}
}
this will reduce the runtime a lot, but it will still take a long time.
another optimization you can do is to not start checking form sum but calculate the smallest number that has 500 divisors.
and then you can find the largest triangle number after that, and start from there.
If you can figure something else special about the nature of this problem, than it is possible for you to reduce the runtime by a whole lot.
The number of divisors of a number is the product of the powers of the prime factors plus one. For example: 28 = 2^2*7^1, so the # of divisors is (2+1)*(1+1) = 6.
This means that, if you want many divisors compared to the size of the number, you don't want any one prime to occur too often. Put another way: it is likely that the smallest triangular number with at least 500 divisors is the product of small powers of small primes.
So, instead of checking every number to see if it divides the triangular number, go through a list of the smallest primes, and see how often each one occurs in the prime factorization. Then use the formula above to compute the number of divisors.
Take these steps:
1.) Calculate the first log(2, 499) prime numbers (not 500, as 1 is counted as a divisor if I am nit mistaken despite the fact that it is not prime, as it has only one divisor). There are many solutions out there, but you catch my drift.
2.) A triangle number is of the form of n * (n + 1) / 2, because
1 + 2 + ... + 100 = (1 + 100) + (2 + 99) + ... + (50 + 51) = 101 * 50 = 101 * 100 / 2 = 5050 (as Cauchy solved it when he was an eight-year boy and the teacher punished him with this task).
1 + ... + n = (1 + n) + (2 + n - 1) + ... = n * (n + 1) / 2.
3.) S = prod(first log(2, 499) prime numbers)
4.) Solve the equation of n * (n + 1) / 2 = S and calculate its ceiling. You will have an integer, let's call it m.
5.)
while (not(found))
found = isCorrect(m)
if (not(found)) then
m = m + 1
end if
end while
return m
and there you go. Let me know if I was able to help you.
As #EricLippert nad #LajosArpad mentioned, the key idea is to iterate over triangle numbers only. You can calculate them using the following formula:
T(n) = n * (n + 1) / 2
Here is JSFiddle which you may find helpful.
function generateTriangleNumber(n) {
return (n * (n + 1)) / 2;
}
function findTriangleNumberWithOver500Divisors() {
var nextTriangleNum;
var sqrt;
for (i = 2;; i++) {
var factors = [];
factors[0] = 1;
nextTriangleNum = generateTriangleNumber(i);
sqrt = Math.pow(nextTriangleNum, 0.5);
sqrt = Math.floor(sqrt);
var j;
for (j = 2; j <= sqrt; j++) {
if (nextTriangleNum % j == 0) {
var quotient = nextTriangleNum / j;
factors[factors.length] = j;
factors[factors.length] = quotient;
}
}
factors[factors.length] = nextTriangleNum;
if (factors.length > 500) {
break;
}
}
console.log(nextTriangleNum);
}
Incidentally, the first Google result for divisors of triangular number search query gives this :)
Project Euler 12: Triangle Number with 500 Divisors
See if it helps.
EDIT: Text from that article:
The first triangle number with over 500 digits is: 76576500 Solution
took 1 ms

Categories