I am currently looking to find the largest amount of consecutive odd integers added together to equal a target number.
My current code to find 3 consecutive integers looks like
public class consecutiveOdd {
public static void main(String[] args){
int target = 160701;
boolean found = false;
for(int i = 1; i < target; i++){
if(i + (i+2) + (i+4) == target){
System.out.print(i + " + " + (i+2) + " + " + (i+4));
found = true;
}
}
if(!found){
System.out.println("Sorry none");
}
}
}
I am thinking there will need to be a while loop building iterations of (i+2) increments but am having trouble with developing a correct algorithm. Any help or tips will be much appreciated!
Best,
Otterman
Let's say that the answer is equal to k (k > 0). Then for some odd i we can write: i + (i + 2) + (i + 4) + ... + (i + 2k - 2) = target. You can see that this is a sum of arithmetic progression, therefore you can use a well known formula to compute it. Applying the formula we can get:
i = target/k - k + 1.
Basing on this formula I would suggest the following algorithm:
Iterate over the value of k.
If target/k - k + 1 is a positive odd integer, update the answer.
Simple implementation.
int answer = -1;
for (int k = 1;; k++) {
int i = target / k - k + 1;
if (i <= 0) {
break;
}
// Check if calculated i, can be the start of 'odd' sequence.
if (target % k == 0 && i % 2 == 1) {
answer = k;
}
}
The running time of this algorithm is O(sqrt(target)).
Looking at the pattern:
For 1 summand, i = target
For 2 summands, the equation is 2*i + 2 = target, so i = (target - 2) / 2
For 3 summands, the equation is 3*i + 6 = target, so i = (target - 6) / 3
For 4 summands, the equation is 4*i + 12 = target, so i = (target - 12) / 4
etc. Clearly i must be an odd integer in all cases.
You could work out the general expression for n summands, and simplify it to show you an algorithm, but you might be able to see an algorithm already...
Applying #rossum's suggestion:
For 1 summand, 2m + 1 = target
For 2 summands, 2m + 1 = (target - 2) / 2, so m = (target - 4) / 4
For 3 summands, 2m + 1 = (target - 6) / 3, so m = (target - 9) / 6
For 4 summands, 2m + 1 = (target - 12) / 4, so m = (target - 16) / 8
The sum of a sequence of n odd integers, can be calculated as the average value (midpoint m) multiplied by the number of values (n), so:
sum = 5 + 7 + 9 = m * n = 7 * 3 = 21
sum = 5 + 7 + 9 + 11 = m * n = 8 * 4 = 32
If n is odd then m will be odd, and if n is even then m will be even.
The first and last numbers of the sequence can be calculated as:
first = m - n + 1 = 8 - 4 + 1 = 5
last = m + n - 1 = 8 + 4 - 1 = 11
Other interesting formulas:
m = sum / n
m = (first + last) / 2
last = first + (n - 1) * 2 = first + 2 * n - 2
m = (first + first + 2 * n - 2) / 2 = first + n - 1
The longest sequence would have to start with the lowest possible first value, meaning 1, so we get:
sum = m * n = (first + n - 1) * n = n * n
Which means that the longest sequence of any given sum can at most be sqrt(sum) long.
So starting at sqrt(sum), and searching down until we find a valid n:
/**
* Returns length of sequence, or 0 if no sequence can be found
*/
private static int findLongestConsecutiveOddIntegers(int sum) {
for (int n = (int)Math.sqrt(sum); n > 1; n--) {
if (sum % n == 0) { // m must be an integer
int m = sum / n;
if ((n & 1) == (m & 1)) // If n is odd, mid must be odd. If n is even, m must be even.
return n;
}
}
return 0;
}
Result:
n = findLongestConsecutiveOddIntegers(160701) = 391
m = sum / n = 160701 / 391 = 411
first = m - n + 1 = 411 - 391 + 1 = 21
last = m + n - 1 = 411 + 391 - 1 = 801
Since sqrt(160701) = 400.875..., the result was found in 10 iterations (400 to 391, inclusive).
Conclusion:
Largest Amount of Consecutive Odd Integers to Equal 160701: 391
21 + 23 + 25 + ... + 799 + 801 = 160701
Related
Consider the character string generated by the following rule:
F[0] = "A"
F[1] = "B"
...
F[n] = F[n-1] + F[n-2] with n > 1
Given two positive integers n and k. Let's count the number of characters 'B' in the first k positions of string F[n].
I came up with this idea and got time limit exceeded error:
public class Solution {
public static long[] F = new long[50];
public static Scanner input = new Scanner(System.in);
public static long count(int n, long k) {
if (n == 0 || k == 0) return 0;
else if (n == 1) return 1;
else {
if (k > F[n - 1]) return count(n - 1, F[n - 1]) + count(n - 2, k - F[n - 1]);
else return count(n - 1, k);
}
}
public static void main(String[] args) {
F[0] = 1; F[1] = 1;
for (int i = 2; i < 46; i++) F[i] = F[i - 2] + F[i - 1];
int T = input.nextInt();
while (T-- > 0) {
int n = input.nextInt();
long k = input.nextLong();
System.out.println(count(n, k));
}
}
}
Can someone help me to improve time complexity? Seems my solution has O(n^2) time complexity.
Test case for this question:
Input
Output
4
0 1
0
1 1
1
3 2
1
7 7
4
There seems to be a pattern related to Fibonacci numbers:
A
B
AB 1 + 1 (A count + B count)
BAB 1 + 2
ABBAB 2 + 3
BABABBAB 3 + 5
ABBABBABABBAB 5 + 8
^ k = 7
BABABBAB 3 + 5
^ k = 2 (result = 3)
BAB 1 + 2
^ k = 2 (result = 4)
AB 1 + 1
^ k = 1 = A (result = 4)
Let g(l, r, k) represent the count of Bs in the first k positions of Fib[n] = l + r. Then:
g(l, r, k):
if (1, 1) == (l, r):
return 1 if k == 2 else 0
if (1, 2) == (l, r):
return 1 if k < 3 else 2
ll, rl = getFibSummands(l)
lr, rr = getFibSummands(r)
if k > l:
return rl + g(lr, rr, k - l)
return g(ll, rl, k)
This answer above may have misinterpreted the starting order of concatenation, which possibly should be BA, in which case, we would need to reverse l, r.
A
B
BA 1 + 1 (B count + A count)
BAB 2 + 1
BABBA 3 + 2
BABBABAB 5 + 3
BABBABABBABBA 8 + 5
^ k = 7
BABBABAB
^ k = 7
BAB
^ k = 2 (result = 3)
BA
^ k = 2
A
^ k = 1 (result = 4)
g(l, r, k):
if (1, 1) == (l, r):
return 1 if k == 2 else 0
if (2, 1) == (l, r):
return 1 if k < 3 else 2
ll, rl = getFibSummands(l)
lr, rr = getFibSummands(r)
if k > l:
return ll + g(lr, rr, k - l)
return g(ll, rl, k)
For clarity, define Fib(n) to be the Fibonacci sequence where Fib(0) = Fib(1) = 1, and Fib(n+2) = Fib(n+1) + Fib(n).
Note that F[n] has Fib(n) characters, with Fib(n-1) of them being Bs.
Let C(n, k) be the number of B's in the first k characters of F[n].
The base cases are obvious
C(0, k) = 0
C(n, k) = 0 if k<=0
C(1, 1) = 1
C(2, k) = 1
In general:
C(n, k) = C(n-1, k) if k <= Fib(n-1)
= Fib(n-2) + C(n-1, k - Fib(n-1)) otherwise
This is the observation that F[n] = F[n-1] + F[n-2] and k lies in either the first part or the second. The first part has Fib(n-1) characters of which Fib(n-2) of them are B's.
If you precompute the Fibonacci numbers from 0 to n, then you can compute C(n, k) in O(n) arithmetic operations.
You tagged java, but here's a python solution including test cases:
def C(n, k):
if n == 0: return 0
total = 0
fib = [1, 1]
while len(fib) <= n and fib[-1] <= k:
fib.append(fib[-1] + fib[-2])
n = min(n, len(fib) - 1)
while True:
if n <= 2:
return total + 1
elif k <= fib[n-1]:
n -= 1
else:
total += fib[n-2]
k -= fib[n-1]
n -= 1
tcs = [
([0, 1], 0),
([1, 1], 1),
([3, 2], 1),
([7, 7], 4)
]
for (n, k), want in tcs:
got = C(n, k)
if got != want:
print('C(%d, %d) = %d, want %d' % (n, k, got, want))
This includes an optimization which reduces n so that initially k > Fib(n-1). This makes the code O(min(n, log(k))) arithmetic operations.
sum=0;
for(i=1; i<n;i++)
for( j = 1; j < i * i; j++ )
if( j % i == 0 )
for( k = 0; k < j; k++ )
sum++;
I know that the outer most loop executes n time, the next loop n^2 times, but how do I factor the j%i with the loop? The correct answer is O(N^4), but I am not sure how that is reached.
Since we know that the k loop only executes when j is divisible by i (this is where the modulo comes in), we can figure out how many times k loops at every i loop:
i = 2, j:1..4: loops 2 times (j=2).
i = 3, j:1..9: loops 3 + 6 times (j=3,6).
i = 4, j:1..16: loops 4 + 8 + 12 times (j=4,8,12).
...
i = n, j:1..n*n: loops n + 2n + ... (n - 1) * n
Or Sum(k * n, k = 1, n - 1) = n^2 * (n - 1) / 2 times.
I realize that this should stop at i = n - 1, but this is enough to show the growth.
This means that our complexity is that of
2 + (3 + 6) + (4 + 8 + 12) + ... + (n^2 * (n - 1) / 2)
=Sum(
i^2 * (i - 1) / 2,
i = 2, n - 1
)
=Sum(
(i^3 - i^2) / 2,
i = 2, n - 1
)
Since the i^3 term dominates and the division by 2 doesn't matter, this has the same time complexity as
Sum(i^3, i=2, n - 1)
Which is O(n^4).
I was going through a simple program that takes a number and finds the number of occurrences of consecutive numbers that matches with given number.
For example:
if input is 15, then the consecutive numbers that sum upto 15 are:
1,2,3,4,5
4,5,6
7,8
So the answer is 3 as we have 3 possibilities here.
When I was looking for a solution I found out below answer:
static long process(long input) {
long count = 0;
for (long j = 2; j < input/ 2; j++) {
long temp = (j * (j + 1)) / 2;
if (temp > input) {
break;
}
if ((input- temp) % j == 0) {
count++;
}
}
return count;
}
I am not able to understand how this solves the requirement because this program is using some formula which I am not able to understand properly, below are my doubts:
The for loop starts from 2, what is the reason for this?
long temp = (j * (j + 1)) / 2; What does this logic indicates? How is this helpful to solving the problem?
if ((num - temp) % j == 0) Also what does this indicate?
Please help me in understanding this solution.
I will try to explain this as simple as possible.
If input is 15, then the consecutive numbers that sum upto 15 are:
{1,2,3,4,5} -> 5 numbers
{4,5,6} -> 3 numbers
{7,8} -> 2 numbers
At worst case, this must be less than the Sum of 1st n natural numbers = (n*(n+1) /2.
So for a number 15, there can never be a combination of 6 consecutive numbers summing up to 15 as the sum of 1st 6 numbers =21 which is greater than 15.
Calculate temp: This is (j*(j+1))/2.
Take an example. Let input = 15. Let j =2.
temp = 2*3/2 = 3; #Meaning 1+2 =3
For a 2-number pair, let the 2 terms be 'a+1' and 'a+2'.(Because we know that the numbers are consecutive.)
Now, according to the question, the sum must add up to the number.
This means 2a+3 =15;
And if (15-3) is divisible by 2, 'a' can be found. a=6 -> a+1=7 and a+2=8
Similarly, let a+1 ,a+2 and a+3
a + 1 + a + 2 + a + 3 = 15
3a + 6 = 15
(15-6) must be divisible by 3.
Finally, for 5 consecutive numbers a+1,a+2,a+3,a+4,a+5 , we have
5a + 15 = 15;
(15-15) must be divisible by 5.
So, the count will be changed for j =2,3 and 5 when the input is 15
If the loop were to start from 1, then we would be counting 1 number set too -> {15} which is not needed
To summarize:
1) The for loop starts from 2, what is the reason for this?
We are not worried about 1-number set here.
2) long temp = (j * (j + 1)) / 2; What does this logic indicates? How is this helpful to solving the problem?
This is because of the sum of 1st n natural numbers property as I have
explained the above by taking a+1 and a+2 as 2 consecutive
numbers.
3) if ((num - temp) % j == 0) Also what does this indicate?
This indicates the logic that the input subtracted from the sum of 1st
j natural numbers must be divisible by j.
We need to find all as and ns, that for given b the following is true:
a + (a + 1) + (a + 2) + ... (a + (n - 1)) = b
The left side is an arithmetic progression and can be written as:
(a + (n - 1) / 2) * n = b (*)
To find the limit value of n, we know, that a > 0, so:
(1 + (n - 1) / 2) * n = n(n + 1) / 2 <= b
n(n + 1) <= 2b
n^2 + n + 1/4 <= 2b + 1/4
(n + 1/2)^2 <= 2b + 1/4
n <= sqrt(2b + 1/4) - 1/2
Now we can rewrite (*) to get formula for a:
a = b / n - (n - 1) / 2
Example for b = 15 and n = 3:
15 / 3 - (3 - 1) / 2 = 4 => 4 + 5 + 6 = 15
And now the code:
double b = 15;
for (double n = 2; n <= Math.ceil(Math.sqrt(2 * b + .25) - .5); n++) {
double candidate = b / n - (n - 1) / 2;
if (candidate == (int) candidate) {
System.out.println("" + candidate + IntStream.range(1, (int) n).mapToObj(i -> " + " + (candidate + i)).reduce((s1, s2) -> s1 + s2).get() + " = " + b);
}
}
The result is:
7.0 + 8.0 = 15.0
4.0 + 5.0 + 6.0 = 15.0
1.0 + 2.0 + 3.0 + 4.0 + 5.0 = 15.0
We are looking for consecutive numbers that sum up to the given number.
It's quite obvious that there could be at most one series with a given length, so basically we are looking for those values witch could be the length of such a series.
variable 'j' is the tested length. It starts from 2 because the series must be at least 2 long.
variable 'temp' is the sum of a arithmetic progression from 1 to 'j'.
If there is a proper series then let X the first element. In this case 'input' = j*(X-1) + temp.
(So if temp> input then we finished)
At the last line it checks if there is an integer solution of the equation. If there is, then increase the counter, because there is a series with j element which is a solution.
Actually the solution is wrong, because it won't find solution if input = 3. (It will terminate immediately.) the cycle should be:
for(long j=2;;j++)
The other condition terminates the cycle faster anyway.
NB: loop is starting from 2 because=> (1*(1+1))/2 == 1, which doesn't make sense, i.e, it doesn't effect on the progress;
let, k = 21;
so loop will iterate upto (k/2) => 10 times;
temp = (j*(j+1))/2 => which is, 3 when j =2, 6 when j = 3, and so on (it calculates sum of N natural numbers)
temp > k => will break the loop because, we don't need to iterate the loop when we got 'sum' which is more than 'K'
((k-temp)%j) == 0 => it is basically true when the input subtracted from the sum of first j natural numbers are be divisible by j, if so then increment the count to get total numbers of such equation!
public static long process(long input) {
long count = 0, rest_of_sum;
for (long length = 2; length < input / 2; length++) {
long partial_sum = (length * (length + 1)) / 2;
if (partial_sum > input) {
break;
}
rest_of_sum = input - partial_sum
if (rest_of_sum % length == 0)
count++;
}
return count;
}
input - given input number here it is 15
length - consecutive numbers length this is at-least 2 at max input/2
partial_sum = sum of numbers from 1 to length (which is a*(a+1)/2 for 1 to a numbers) assume this is a partial sequence
rest_of_sum = indicates the balance left in input
if rest of sum is multiple of length meaning is that we can add (rest_of_sum/length) to our partial sequence
lets call (rest_of_sum/length) as k
this only means we can build a sequence here that sums up to our input number
starting with (k+1) , (k+2), ... (k+length)
this can validated now
(k+1) + (k+2) + ... (k+length)
we can reduce this as k+k+k+.. length times + (1+2+3..length)
can be reduced as => k* length + partial_sum
can be reduced as => input (since we verified this now)
So idea here is to increment count every-time we find a length which satisfies this case here
If you put this tweak in it may fix code. I have not extensively tested it. It's an odd one but it puts the code through an extra iteration to fix the early miscalculations. Even 1/20000 would work! Had this been done with floats that got rounded down and 1 added to them I think that would have worked too:
for (long j = 2; j < input+ (1/2); j++) {
In essence you need to only know one formula:
The sum of the numbers m..n (or m to n) (and where n>m in code)
This is ((n-m+1)*(n+m))/2
As I have commented already the code in the original question was bugged.
See here.
Trying feeding it 3. That has 1 occurrence of the consecutive numbers 1,2. It yields 0.
Or 5. That has 2,3 - should yield 1 too - gives 0.
Or 6. This has 1,2,3 - should yield 1 too - gives 0.
In your original code, temp or (j * (j + 1)) / 2 represented the sum of the numbers 1 to j.
1 2 3 4 5
5 4 3 2 1
=======
6 6 6 6 6 => (5 x 6) /2 => 30/2 => 15
As I have shown in the code below - use System.out.println(); to spew out debugging info.
If you want to perfect it make sure m and n's upper limits are half i, and i+1 respectively, rounding down if odd. e.g: (i=15 -> m=7 & n=8)
The code:
class Playground {
private static class CountRes {
String ranges;
long count;
CountRes(String ranges, long count) {
this.ranges = ranges;
this.count = count;
}
String getRanges() {
return this.ranges;
}
long getCount() {
return this.count;
}
}
static long sumMtoN(long m, long n) {
return ((n-m+1)* (n+m))/2;
}
static Playground.CountRes countConsecutiveSums(long i, boolean d) {
long count = 0;
StringBuilder res = new StringBuilder("[");
for (long m = 1; m< 10; m++) {
for (long n = m+1; n<=10; n++) {
long r = Playground.sumMtoN(m,n);
if (d) {
System.out.println(String.format("%d..%d %d",m,n, r));
}
if (i == r) {
count++;
StringBuilder s = new StringBuilder(String.format("[%d..%d], ",m,n));
res.append(s);
}
}
}
if (res.length() > 2) {
res = new StringBuilder(res.substring(0,res.length()-2));
}
res.append("]");
return new CountRes(res.toString(), count);
}
public static void main(String[ ] args) {
Playground.CountRes o = countConsecutiveSums(3, true);
for (long i=3; i<=15; i++) {
o = Playground.countConsecutiveSums(i,false);
System.out.println(String.format("i: %d Count: %d Instances: %s", i, o.getCount(), o.getRanges()));
}
}
}
You can try running it here
The output:
1..2 3
1..3 6
1..4 10
1..5 15
1..6 21
1..7 28
1..8 36
1..9 45
1..10 55
2..3 5
2..4 9
2..5 14
2..6 20
2..7 27
2..8 35
2..9 44
2..10 54
3..4 7
3..5 12
3..6 18
3..7 25
3..8 33
3..9 42
3..10 52
4..5 9
4..6 15
4..7 22
4..8 30
4..9 39
4..10 49
5..6 11
5..7 18
5..8 26
5..9 35
5..10 45
6..7 13
6..8 21
6..9 30
6..10 40
7..8 15
7..9 24
7..10 34
8..9 17
8..10 27
9..10 19
i: 3 Count: 1 Instances: [[1..2]]
i: 4 Count: 0 Instances: []
i: 5 Count: 1 Instances: [[2..3]]
i: 6 Count: 1 Instances: [[1..3]]
i: 7 Count: 1 Instances: [[3..4]]
i: 8 Count: 0 Instances: []
i: 9 Count: 2 Instances: [[2..4], [4..5]]
i: 10 Count: 1 Instances: [[1..4]]
i: 11 Count: 1 Instances: [[5..6]]
i: 12 Count: 1 Instances: [[3..5]]
i: 13 Count: 1 Instances: [[6..7]]
i: 14 Count: 1 Instances: [[2..5]]
i: 15 Count: 3 Instances: [[1..5], [4..6], [7..8]]
I need to find the number of heavy integers between two integers A and B, where A <= B at all times.
An integer is considered heavy whenever the average of it's digit is larger than 7.
For example: 9878 is considered heavy, because (9 + 8 + 7 + 8)/4 = 8
, while 1111 is not, since (1 + 1 + 1 + 1)/4 = 1.
I have the solution below, but it's absolutely terrible and it times out when run with large inputs. What can I do to make it more efficient?
int countHeavy(int A, int B) {
int countHeavy = 0;
while(A <= B){
if(averageOfDigits(A) > 7){
countHeavy++;
}
A++;
}
return countHeavy;
}
float averageOfDigits(int a) {
float result = 0;
int count = 0;
while (a > 0) {
result += (a % 10);
count++;
a = a / 10;
}
return result / count;
}
Counting the numbers with a look-up table
You can generate a table that stores how many integers with d digits have a sum of their digits that is greater than a number x. Then, you can quickly look up how many heavy numbers there are in any range of 10, 100, 1000 ... integers. These tables hold only 9×d values, so they take up very little space and can be quickly generated.
Then, to check a range A-B where B has d digits, you build the tables for 1 to d-1 digits, and then you split the range A-B into chunks of 10, 100, 1000 ... and look up the values in the tables, e.g. for the range A = 782, B = 4321:
RANGE DIGITS TARGET LOOKUP VALUE
782 - 789 78x > 6 table[1][ 6] 3 <- incomplete range: 2-9
790 - 799 79x > 5 table[1][ 5] 4
800 - 899 8xx >13 table[2][13] 15
900 - 999 9xx >12 table[2][12] 21
1000 - 1999 1xxx >27 table[3][27] 0
2000 - 2999 2xxx >26 table[3][26] 1
3000 - 3999 3xxx >25 table[3][25] 4
4000 - 4099 40xx >24 impossible 0
4100 - 4199 41xx >23 impossible 0
4200 - 4299 42xx >22 impossible 0
4300 - 4309 430x >21 impossible 0
4310 - 4319 431x >20 impossible 0
4320 - 4321 432x >19 impossible 0 <- incomplete range: 0-1
--
48
If the first and last range are incomplete (not *0 - *9), check the starting value or the end value against the target. (In the example, 2 is not greater than 6, so all 3 heavy numbers are included in the range.)
Generating the look-up table
For 1-digit decimal integers, the number of integers n that is greater than value x is:
x: 0 1 2 3 4 5 6 7 8 9
n: 9 8 7 6 5 4 3 2 1 0
As you can see, this is easily calculated by taking n = 9-x.
For 2-digit decimal integers, the number of integers n whose sum of digits is greater than value x is:
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
n: 99 97 94 90 85 79 72 64 55 45 36 28 21 15 10 6 3 1 0
For 3-digit decimal integers, the number of integers n whose sum of digits is greater than value x is:
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
n: 999 996 990 980 965 944 916 880 835 780 717 648 575 500 425 352 283 220 165 120 84 56 35 20 10 4 1 0
Each of these sequences can be generated from the previous one: start with value 10d and then subtract from this value the previous sequence in reverse (skipping the first zero). E.g. to generate the sequence for 3 digits from the sequence for 2 digits, start with 103 = 1000, and then:
0. 1000 - 1 = 999
1. 999 - 3 = 996
2. 996 - 6 = 990
3. 990 - 10 = 980
4. 980 - 15 = 965
5. 965 - 21 = 944
6. 944 - 28 = 916
7. 916 - 36 = 880
8. 880 - 45 = 835
9. 835 - 55 = 780
10. 780 - 64 + 1 = 717 <- after 10 steps, start adding the previous sequence again
11. 717 - 72 + 3 = 648
12. 648 - 79 + 6 = 575
13. 575 - 85 + 10 = 500
14. 500 - 90 + 15 = 425
15. 425 - 94 + 21 = 352
16. 352 - 97 + 28 = 283
17. 283 - 99 + 36 = 220
18. 220 - 100 + 45 = 165 <- at the end of the sequence, keep subtracting 10^(d-1)
19. 165 - 100 + 55 = 120
20. 120 - 100 + 64 = 84
21. 84 - 100 + 72 = 56
22. 56 - 100 + 79 = 35
23. 35 - 100 + 85 = 20
24. 20 - 100 + 90 = 10
25. 10 - 100 + 94 = 4
26. 4 - 100 + 97 = 1
27. 1 - 100 + 99 = 0
By the way, you can use the same tables if "heavy" numbers are defined with a value other than 7.
Code example
Below is a Javascript code snippet (I don't speak Java) that demonstrates the method. It is very much unoptimised, but it does the 0→100,000,000 example in less than 0.07ms. It also works for weights other than 7. Translated to Java, it should easily beat any algorithm that actually runs through the numbers and checks their weight.
function countHeavy(A, B, weight) {
var a = decimalDigits(A), b = decimalDigits(B); // create arrays
while (a.length < b.length) a.push(0); // add leading zeros
var digits = b.length, table = weightTable(); // create table
var count = 0, diff = B - A + 1, d = 0; // calculate range
for (var i = digits - 1; i >= 0; i--) if (a[i]) d = i; // lowest non-0 digit
while (diff) { // increment a until a=b
while (a[d] == 10) { // move to higher digit
a[d++] = 0;
++a[d]; // carry 1
}
var step = Math.pow(10, d); // value of digit d
if (step <= diff) {
diff -= step;
count += increment(d); // increment digit d
}
else --d; // move to lower digit
}
return count;
function weightTable() { // see above for details
var t = [[],[9,8,7,6,5,4,3,2,1,0]];
for (var i = 2; i < digits; i++) {
var total = Math.pow(10, i), final = total / 10;
t[i] = [];
for (var j = 9 * i; total > 0; --j) {
if (j > 9) total -= t[i - 1][j - 10]; else total -= final;
if (j < 9 * (i - 1)) total += t[i - 1][j];
t[i].push(total);
}
}
return t;
}
function increment(d) {
var sum = 0, size = digits;
for (var i = digits - 1; i >= d; i--) {
if (a[i] == 0 && i == size - 1) size = i; // count used digits
sum += a[i]; // sum of digits
}
++a[d];
var target = weight * size - sum;
if (d == 0) return (target < 0) ? 1 : 0; // if d is lowest digit
if (target < 0) return table[d][0] + 1; // whole range is heavy
return (target > 9 * d) ? 0 : table[d][target]; // use look-up table
}
function decimalDigits(n) {
var array = [];
do {array.push(n % 10);
n = Math.floor(n / 10);
} while (n);
return array;
}
}
document.write("0 → 100,000,000 = " + countHeavy(0, 100000000, 7) + "<br>");
document.write("782 → 4321 = " + countHeavy(782, 4321, 7) + "<br>");
document.write("782 → 4321 = " + countHeavy(782, 4321, 5) + " (weight: 5)");
I really liked the post of #m69 so I wrote implementation inspired by it. The table creation is not that elegant, but works. For n+1 digits long integer I sum (at most) 10 values from n digits long integer, one for every digit 0-9.
I use this simplification to avoid arbitrary range calculation:
countHeavy(A, B) = countHeavy(0, B) - countHeavy(0, A-1)
The result is calculated in two loops. One for numbers shorter than the given number and one for the rest. I was not able to merge them easily. getResultis just lookup into the tablewith range checking, the rest of the code should be quite obvious.
public class HeavyNumbers {
private static int maxDigits = String.valueOf(Long.MAX_VALUE).length();
private int[][] table = null;
public HeavyNumbers(){
table = new int[maxDigits + 1][];
table[0] = new int[]{1};
for (int s = 1; s < maxDigits + 1; ++s) {
table[s] = new int[s * 9 + 1];
for (int k = 0; k < table[s].length; ++k) {
for (int d = 0; d < 10; ++d) {
if (table[s - 1].length > k - d) {
table[s][k] += table[s - 1][Math.max(0, k - d)];
}
}
}
}
}
private int[] getNumberAsArray(long number) {
int[] tmp = new int[maxDigits];
int cnt = 0;
while (number != 0) {
int remainder = (int) (number % 10);
tmp[cnt++] = remainder;
number = number / 10;
}
int[] ret = new int[cnt];
for (int i = 0; i < cnt; ++i) {
ret[i] = tmp[i];
}
return ret;
}
private int getResult(int[] sum, int digits, int fixDigitSum, int heavyThreshold) {
int target = heavyThreshold * digits - fixDigitSum + 1;
if (target < sum.length) {
return sum[Math.max(0, target)];
}
return 0;
}
public int getHeavyNumbersCount(long toNumberIncl, int heavyThreshold) {
if (toNumberIncl <= 0) return 0;
int[] numberAsArray = getNumberAsArray(toNumberIncl);
int res = 0;
for (int i = 0; i < numberAsArray.length - 1; ++i) {
for (int d = 1; d < 10; ++d) {
res += getResult(table[i], i + 1, d, heavyThreshold);
}
}
int fixDigitSum = 0;
int fromDigit = 1;
for (int i = numberAsArray.length - 1; i >= 0; --i) {
int toDigit = numberAsArray[i];
if (i == 0) {
toDigit++;
}
for (int d = fromDigit; d < toDigit; ++d) {
res += getResult(table[i], numberAsArray.length, fixDigitSum + d, heavyThreshold);
}
fixDigitSum += numberAsArray[i];
fromDigit = 0;
}
return res;
}
public int getHeavyNumbersCount(long fromIncl, long toIncl, int heavyThreshold) {
return getHeavyNumbersCount(toIncl, heavyThreshold) -
getHeavyNumbersCount(fromIncl - 1, heavyThreshold);
}
}
It is used like this:
HeavyNumbers h = new HeavyNumbers();
System.out.println( h.getHeavyNumbersCount(100000000,7));
prints out 569484, the repeated calculation time without initialization of the table is under 1us
I looked at the problem differently than you did. My perception is that the problem is based on the base-10 representation of a number, so the first thing you should do is to put the number into a base-10 representation. There may be a nicer way of doing it, but Java Strings represent Integers in base-10, so I used those. It's actually pretty fast to turn a single character into an integer, so this doesn't really cost much time.
Most importantly, your calculations in this matter never need to use division or floats. The problem is, at its core, about integers only. Do all the digits (integers) in the number (integer) add up to a value greater than or equal to seven (integer) times the number of digits (integer)?
Caveat - I don't claim that this is the fastest possible way of doing it, but this is probably faster than your original approach.
Here is my code:
package heavyNum;
public class HeavyNum
{
public static void main(String[] args)
{
HeavyNum hn = new HeavyNum();
long startTime = System.currentTimeMillis();
hn.countHeavy(100000000, 1);
long endTime = System.currentTimeMillis();
System.out.println("Time elapsed: "+(endTime- startTime));
}
private void countHeavy(int A, int B)
{
int heavyFound = 0;
for(int i = B+1; i < A; i++)
{
if(isHeavy(i))
heavyFound++;
}
System.out.println("Found "+heavyFound+" heavy numbers");
}
private boolean isHeavy(int i)
{
String asString = Integer.valueOf(i).toString();
int length = asString.length();
int dividingLine = length * 7, currTotal = 0, counter = 0;
while(counter < length)
{
currTotal += Character.getNumericValue(asString.charAt(counter++));
}
return currTotal > dividingLine;
}
}
Credit goes to this SO Question for how I get the number of digits in an integer and this SO Question for how to quickly convert characters to integers in java
Running on a powerful computer with no debugger for numbers between one and 100,000,000 resulted in this output:
Found 569484 heavy numbers
Time elapsed: 6985
EDIT: I initially was looking for numbers whose digits were greater than or equal to 7x the number of digits. I previously had results of 843,453 numbers in 7025 milliseconds.
Here's a pretty barebones recursion with memoization that enumerates the digit possibilities one by one for a fixed-digit number. You may be able to set A and B by controlling the range of i when calculating the corresponding number of digits.
Seems pretty fast (see the result for 20 digits).
JavaScript code:
var hash = {}
function f(k,soFar,count){
if (k == 0){
return 1;
}
var key = [k,soFar].join(",");
if (hash[key]){
return hash[key];
}
var res = 0;
for (var i=Math.max(count==0?1:0,7*(k+count)+1-soFar-9*(k-1)); i<=9; i++){
res += f(k-1,soFar+i,count+1);
}
return hash[key] = res;
}
// Output:
console.log(f(3,0,0)); // 56
hash = {};
console.log(f(6,0,0)); // 12313
hash = {};
console.log(f(20,0,0)); // 2224550892070475
You can indeed use strings to get the number of digits and then add the values of the individual digits to see if their sum > 7 * length, as Jeutnarg seems to do. I took his code and added my own, simple isHeavyRV(int):
private boolean isHeavyRV(int i)
{
int sum = 0, count = 0;
while (i > 0)
{
sum += i % 10;
count++;
i = i / 10;
}
return sum >= count * 7;
}
Now, instead of
if(isHeavy(i))
I tried
if(isHeavyRV(i))
I actually first tested his implementation of isHeavy(), using strings, and that ran in 12388 milliseconds on my machine (an older iMac), and it found 843453 heavy numbers.
Using my implementation, I found exactly the same number of heavy numbers, but in a time of a mere 5416 milliseconds.
Strings may be fast, but they can't beat a simple loop doing basically what Integer.toString(i, 10) does as well, but without the string detour.
When you add 1 to a number, you are incrementing one digit, and changing all the smaller digits to zero. If incrementing changes from a heavy to a non-heavy number, its because too many low-order digits were zeroed. In this case, it's pretty easy to find the next heavy number without checking all the numbers in between:
public class CountHeavy
{
public static void main(String[] args)
{
long startTime = System.currentTimeMillis();
int numHeavy = countHeavy(1, 100000000);
long endTime = System.currentTimeMillis();
System.out.printf("Found %d heavy numbers between 1 and 100000000\n", numHeavy);
System.out.println("Time elapsed: "+(endTime- startTime)+" ms");
}
static int countHeavy(int from, int to)
{
int numdigits=1;
int maxatdigits=9;
int numFound = 0;
if (from<1)
{
from=1;
}
for(int i = from; i < to;)
{
//keep track of number of digits in i
while (i > maxatdigits)
{
long newmax = 10L*maxatdigits+9;
maxatdigits = (int)Math.min(Integer.MAX_VALUE, newmax);
++numdigits;
}
//get sum of digits
int digitsum=0;
for(int digits=i;digits>0;digits/=10)
{
digitsum+=(digits%10);
}
//calculate a step size that increments the first non-zero digit
int step=1;
int stepzeros=0;
while(step <= (Integer.MAX_VALUE/10) && to-i >= step*10 && i%(step*10) == 0)
{
step*=10;
stepzeros+=1;
}
//step is a 1 followed stepzeros zeros
//how much is our sum too small by?
int need = numdigits*7+1 - digitsum;
if (need <= 0)
{
//already have enough. All the numbers between i and i+step are heavy
numFound+=step;
}
else if (need <= stepzeros*9)
{
//increment to the smallest possible heavy number. This puts all the
//needed sum in the lowest-order digits
step = need%9;
for(;need >= 9;need-=9)
{
step = step*10+9;
}
}
//else there are no heavy numbers between i and i+step
i+=step;
}
return numFound;
}
}
Found 569484 heavy numbers between 1 and 100000000
Time elapsed: 31 ms
Note that the answer is different from #JeutNarg's, because you asked for average > 7, not average >= 7.
I need your help guys with this specific question.
How many numbers from an interval a <= b [1, 10^18] are dividable by 2, 3 or 5?
Result must be run in max 1 sec!
A standard program would use a for loop seen bellow.
a = 1;
b = 1000000000
results = 0;
for (int i = a; i <= b; i++){
if (i % 2 == 0 || i % 3 == 0, i % 5 == 0){
results++;
}
}
System.out.println(results);
But if I enter high numbers, my program need a lot of time to give me the results.
Example 1:
a = 11, b = 30, result = 14
Example 2:
a = 123456789012345678, b = 876543210987654321
, result = 552263376115226339
I came up with something like that
public static void main(String[] args) {
long a = 123456789012345678L, b = 876543210987654321L;
long start = System.currentTimeMillis();
long score = getCount(b) - getCount(a - 1);
System.out.println("Time: " + ((System.currentTimeMillis() - start)));
System.out.println("Divisible by 2 or 3 or 5: " + score);
}
public static long getCount(long end) {
return (end / 2) + (end / 3) + (end / 5) - ((end / 6) + (end / 10) + (end / 15)) + (end / 30);
}
The solution:
It counts how many numbers are divisible by 2 or 3 or 5 separately and sums that.
Now we need to discard numbers that where counted twice: for 2 and 3 it will be every 6th number, for 2 and 5 every 10th number, for 3 and 5 every 15th number
At the end we need to include numbers that are divisible by 2 and 3 and 5 that where discarded in step 2 so we add every 30th number.