I'm working on a problem to count the number of 2s between 0 and n in decimal representation, without using a recursive solution. I found the following solution and I'm debugging it, and my question is: what is the logical meaning of seendigits, and statements countof2s += digit * position * pow10_posMinus1 and seendigits = seendigits + pow10_pos * digit? If anyone could offer any insights, that would be great.
public static int count2sI(int num) {
int countof2s = 0, digit = 0;
int j = num, seendigits = 0, position = 0, pow10_pos = 1;
/* maintaining this value instead of calling pow() is an 6x perf
* gain (48s -> 8s) pow10_posMinus1. maintaining this value
* instead of calling Numof2s is an 2x perf gain (8s -> 4s).
* overall > 10x speedup */
while (j > 0) {
digit = j % 10;
int pow10_posMinus1 = pow10_pos / 10;
countof2s += digit * position * pow10_posMinus1;
/* we do this if digit <, >, or = 2
* Digit < 2 implies there are no 2s contributed by this
* digit.
* Digit == 2 implies there are 2 * numof2s contributed by
* the previous position + num of 2s contributed by the
* presence of this 2 */
if (digit == 2) {
countof2s += seendigits + 1;
}
/* Digit > 2 implies there are digit * num of 2s by the prev.
* position + 10^position */
else if (digit > 2) {
countof2s += pow10_pos;
}
seendigits = seendigits + pow10_pos * digit;
pow10_pos *= 10;
position++;
j = j / 10;
}
return (countof2s);
}
The digits are processed from right to left. For the input 2468, the first iteration processes digit 8, the second 6, then 4, and finally 2.
The variable seendigits keeps track of the combined value of the digits that have already been processed; e.g. for the input 2468, the value during the four iterations would be 0, 8, 68 and 468. This value is used when a digit equals 2 (see below).
It is important to note that when processing a digit, e.g. 4 in the example 2468, the number of twos in the lower digits for the range 1 to 68 have already been counted. This count is used for the range 401 to 468, so what still needs to be counted is the number of twos in the lower digits for the range 1 to 400, and the number of twos in the current digit for the range 1 to 468. This algorithm does these two things seperately.
Counting the twos in the lower digits is done in this line:
countof2s += digit * position * pow10_posMinus1;
In the first iteration this adds 0, because there are no lower digits. In the second iteration, this adds digit * 1 * 1, because there is 1 lower digit, and it equals 2 once for every value of the current digit. In the third iteration, this adds digit * 2 * 10, because there are 2 lower digits, and they each equal 2 ten times for every value of the current digit, and so on ...
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 8 * 0 * 0 = 0
iteration 2: digit = 6, add: 6 * 1 * 1 = 6
iteration 3: digit = 4, add: 4 * 2 * 10 = 80
iteration 4: digit = 2, add: 2 * 3 * 100 = 600
e.g. the value 80 is the number of twos in the lower digits of numbers 1 to 400:
Lowest digit of x02, x12, x22, x32, ... x92
These occur 4 times: for 2 to 92, for 102 to 192, for 202 to 292 and for 302 to 392.
Second-lowest digit of x20, x21, x22, x23, ... x29
These occur 4 times: for 20 to 29, for 120 to 129, for 220 to 229 and for 320 to 329.
Counting the twos in the current digit is done in one of the following lines, depending on whether the current digit is greater than or equals 2.
if (digit == 2) { countof2s += seendigits + 1; }
else if (digit > 2) { countof2s += pow10_pos; }
If the current digit is greater than 2, the number of times the digit was two while counting up from 1 depends only on the position of the digit. For position 0, this adds +1, to count the number 2; for the position 1, this adds +10, for the highest digits in the range 20 to 29; for position 2, this adds +100, for the highest digit in the range 200 to 299, and so on ...
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 1
iteration 2: digit = 6, add: 10
iteration 3: digit = 4, add: 100
iteration 4: digit = 2, add: 0 (digit is not greater than 2)
If the current digit equals 2, the number of times the digit was two while counting up from 1 equals the value of the lower digits + 1.
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 0
iteration 2: digit = 6, add: 0
iteration 3: digit = 4, add: 0
iteration 4: digit = 2, add: 469
This is because the current digit equals 2 in the range 2000 to 2468.
UPDATE:
The algorithm is easier to follow if you process the digits left to right: (example in javascript)
function twosInRange(number) {
var digits = Math.floor(Math.log(number) / Math.log(10));
var count = 0;
for (var pos = digits; pos >= 0; --pos) { // 3, 2, 1, 0
var unit = Math.pow(10, pos); // 1000, 100, 10, 1
var digit = Math.floor(number / unit); // 2, 4, 6, 8
number -= digit * unit; // 468, 68, 8, 0
// COUNT OCCURRENCES IN LOWER DIGITS:
count += digit * pos * (unit / 10); // + 2*3*100, 4*2*10, 6*1*1, 8*0*0
// COUNT OCCURRENCES IN CURRENT DIGIT:
if (digit > 2) count += unit; // + (1000), 100, 10, 1
else if (digit == 2) count += number + 1; // + 469, (69), (9), (1)
}
return (count); // 600 + 80 + 6 + 100 + 10 + 1 + 469
}
document.write(twosInRange(2468)); // = 1266
Related
Can somebody help me with this problem?
Statement: - What is the maximum possible n digit number starting from 0 we can make in K steps
using only 2 operations:-
multiplying by 3 or incrementing by 2.
EXAMPLE :
N =2 K = 5;
-> (0->2->6->8->24->72) 72 IS THE ANSWER
N = 2 , K = 51 -> (0->2->6->8->10->30->32->96->98). 98 is the maximum we can get so need to check for rest of the moves.
My 2 state-recursive solution:-
public static void largestNDigitNumber(long[] highest, long maxValue, long k, long currentValue) {
if (highest[0] == (maxValue - 2)) return; //no need to do anything as we get 98 as highest.
if (k < 0) return; //checking for steps
if (highest[0] < currentValue && currentValue <= (maxValue - 2)) {
highest[0] = currentValue;
}
largestNDigitNumber(highest, maxValue, (k - 1), (currentValue * 3));
largestNDigitNumber(highest, maxValue, (k - 1), (currentValue + 2));
}
public static void main(String[] args) {
int n = 2;
long k = 51;
long maxValue = (long) Math.pow(10, n);
long[] highest = new long[1];
largestNDigitNumber(highest, maxValue, (k - 1), 2);
if (highest[0] < (long) Math.pow(10, (n - 1))) {
System.out.println("-1"); // if it is not possible to make n digit in given steps
} else System.out.println(highest[0]);
}
when "k" is small it is giving the correct answer but for bigger values of "k", it does not show any input. for n=2 and k = 51, it does not show anything.
please help me to improve this code
The question is equivalent to asking what is the largest base 3 number that is less than 10^n/2, and has digit sum plus length less than or equal to k+1. (The answer is then double the base 3 number).
For example, N=2 K=5. What's the largest base 3 number that's less than 50, with length plus digit sum less than or equal to 6. Answer: 1100 (36 decimal), so the answer to the original question is 36*2=72.
For N=2, K=51, the largest base-3 number that's less than 50 is 2001 (49 decimal) and has length sum plus digit sum = 7, which is way less than K+1.
Given this representation, it's easy to solve the problem in O(n) time (in fact, you can solve it using pencil and paper). The length d of the base-3 number is as large as possible such that 3^d < 10^n/2 and d<=K. Then fill in the digits of the number greedily from the most-significant first until you have digit sum K+1-d (or you run out of digits).
Equivalence
First note that without loss of generality you can assume you never have three +2 operations in a row, since that can be done more efficiently by inserting a single +2 operation to before the most recent *3 (or simply replacing it by +2 * 3 if there's no *3 operation). Suppose you have represented the current number as a doubled base-3 number. A +2 operation corresponds to adding 1 to the bottom digit (this never overflows into the next column thanks to the observation above). A *3 operation moves all the digits up one column, introducing a 0 as the bottom digit. Note that because the number is doubled, the +2 operation adds just 1 to the base-3 number!
From this, you can see that you can count the number of operations from observation of the doubled base-3 number. Because *3 introduces a new digit, and +2 increases the digit sum by 1, so the number of operations is equal to the number of digits plus 1, plus the digit sum.
As an example. Suppose you have the doubled base-3 number 2 * 2101, then this is equivalent to 2 * (1+3*3*(1+3*(1+1)))) = (2 + 3*3*(2+3*(2+2))).
I tried something like this. it seems to work fine.
getMaxNumber(2, 5) ==> 72
getMaxNumber(2, 51) ==> 98
private int getMaxNumber(int n, int k){
int N = 0;
for (int i = 0; i < n; i++) {
N = N * 10 + 9;
}
int[] result = new int[1];
helper(N, k, 0, 0, result);
return result[0];
}
private void helper(int N, int K, int n, int k, int[] result){
if(n > N) return;
if(k <= K){
result[0] = Math.max(result[0], n);
}
if(n > 0)
helper(N, K, n * 3, k + 1, result);
helper(N, K, n + 2, k + 1, result);
}
Keeping with the style of your original recursive method. I modified it a bit to produce a working solution:
public static long largestNDigitNumber(int n, long currentK, long maxK, long currentValue) {
if (currentK > maxK || n < 1 || maxK < 1) return 0;
if (currentValue >= Math.pow(10, n))
return 0;
long c1 = largestNDigitNumber(n, currentK + 1, maxK, currentValue * 3);
long c2 = largestNDigitNumber(n, currentK + 1, maxK, currentValue + 2);
if (c1 == 0 && c2 == 0)
return currentValue;
return c1 > c2 ? c1 : c2;
}
public static void main(String[] args) {
int n = 2;
long k = 51;
long largest = largestNDigitNumber(n, 0, k, 0);
System.out.println(largest); //98
}
This recursive method returns values here instead of using an array. Hence the check if one returned value is bigger than the other or they are both 0 before returning.
Both the +2 and *3 operations preserve odd/even parity, so starting from 0 we can only reach even numbers. We could start our search at the highest even number: 8, 98, 998, 9998 etc. and see what the shortest distance to 0 is.
If we are looking for the shortest distance, then there are less choices to make. If the current number is a multiple of 3 then there are two choices, either we divide by 3 or subtract 2. Otherwise the only choice is to subtract 2. I suspect that in the majority of cases, dividing by 3 is the better option, so that might be the first to try to keep the tree smaller.
If the minimum number of steps is less than K then as many divide by 3 operations as needed can be used to make the correct K
If the minimum number of steps is equal to K then the problem is solved.
If the minimum number of steps is more than K then you need to pick a lower starting number. Some even numbers will already have been covered as part of the initial calculation. You get those 'for free', provide you include a small amount of record keeping. You only need to examine large even numbers that were missed earlier due to a 'divide by 3' step.
class Armstrong {
public static void main(String[] args) {
int low = 999, high = 99999;
for(int number = low + 1; number < high; ++number) {
int digits = 0;
int result = 0;
int originalNumber = number;
// number of digits calculation
while (originalNumber != 0) {
originalNumber /= 10;
++digits;
}
originalNumber = number;
// result contains sum of nth power of its digits
while (originalNumber != 0) {
int remainder = originalNumber % 10;
result += Math.pow(remainder, digits);
originalNumber /= 10;
}
if (result == number)
System.out.print(number + " ");
}
}
How does this program work? It would be very helpful. Program to find Armstrong between two intervals? Can someone explain step by step? Help me out.
Steb-by-step Explanantion
Initialize the start and end of the interval with 999 and 9999
You can change these numbers but ensure that low is always lesser than high:
int low = 999;
int high = 99999;
for (int number = low + 1; // Create a variable number and assign it one number greater than low
number < high; // This loop should keep repeating until number is less than high(end of the interval)
++number // After each repetition, increment the value of number by 1
) {
Create a variable digits to store the number of digits in number.
For example, if number is 100, this will be later set as 3 as the algorithm proceeds:
int digits = 0;
Create a variable result that will store the sum of powers of numbers:
int result = 0;
Copy the value of number to a local variable originalNumber because it has to be modified:
int originalNumber = number;
The logic of calculating number of digits in a number is keep dividing it by 10 until the number is 0.
Every time you divide any number by 10, the last digit is stripped out. So every time the last digit is stripped out, increment digit by 1 (digit++)
// number of digits calculation
while (originalNumber != 0) // Continue this while loop till originalNumber is not 0
{
originalNumber /= 10; // Divide the number by 10. Dividing a number by 10 removes its last digit.
++digits; // One digit was removed in the above step, which means it has to be counted, so increment digit by 1
}
// For example, for originalNumber = 423,
// 423 / 10 = 42 : digits = 1 (first loop)
// 42 / 10 = 4 : digits = 2 (second loop)
// 4 / 10 = 0 : digits = 3 (third loop)
// Now originalNumber has become 0, so the while loop condition originalNumber != 0 will be false and the loop will stop.
// Now we have digits = 3, which is the number of digits in 423
// When program reaches here, digits will have the number of digits in the
// number "originalNumber"
Copy number again to originalNumber because we want to modify the number
again and originalNumber has become 0 because of the digit counting loop above:
originalNumber = number;
An Armstrong number is a number which is equal to the sum of digits to the power of its number of digits.
e.g.: in 153, (1^3) + (5^3) + (3^3) = 153. We are obtaining the cube of each digit(1, 5, 3) because 153 has 3 digits 1, 5, 3.
If it was say 50, we would check ( 5^2 + 0^2) because 50 has 2 digits (50 is not Armstrong, because 25 + 0 = 25 which is not equal to 50)
We saw above dividing a number by 10 removes the last digit (423 / 10 = 42)
Similarly, if we only want this last digit, we can get it by modulus 10 (% 10): (423 % 10 = 3) (42 % 10 = 2) (4 % 10 = 4)
// Let's assume originalNumber is 153
while (originalNumber != 0) // Same as above, keep looping till the originalNumber is not 0(i.e. all digits have been removed)
{
Extract the last digit to remainder, (for 153: 153 % 10 = 3) (for 15: 15 % 10 = 5)
int remainder = originalNumber % 10;
Now we have the last digit, we need to raise this digit to the number of
digits i.e. 3(for 153): (3^3) = 27. We do this using Math.pow(3, 3). Add this number to result. In the end after each loop's number's power is added, at the end of the loop, result will contain their sum:
result += Math.pow(remainder, digits);
originalNumber /= 10; // Remove the last digit (same as above, for 153: 153 / 10 = 15)
// After this line finishes, one rightmost digit will be removed, this will keep
// happening till originalNumber = 153 becomes 15, then 1, then 0.
// When originalNumber is 0, loop stops and we will have the total sum in result
}
For an Armstrong number like 153, result will also contain 153. For a non-Armstrong number it will contain something else.
if (result == number) // For 153, both will be equal.
System.out.print(number + " "); // This will only execute if result is equal to number, or in other words, only if the number is Armstrong
}
Improvements
As per single responsibility principle, each function or class must exactly do one thing. You have a monolithic main() method that:
Defines the interval
Counts the digit of each number in the interval
Calculate the sum of powers of digits
Check if number is Armstrong and print it.
I would prefer breaking down each operation to a separate method, so that it improves readability and maintainability while respecting single-responsibiliy principle:
class Armstrong {
public static int countDigits(int number) {
int digits = 0;
while (number != 0) {
number /= 10;
++digits;
}
return digits;
}
public static int digitPowerSum(int number, int power) {
int result = 0;
while (number != 0) {
int remainder = number % 10;
result += Math.pow(remainder, power);
number /= 10;
}
return result;
}
public static boolean isArmstrong(int number) {
int digits = countDigits(number);
int sum = digitPowerSum(number, digits);
return sum == number ; // will return true if both numbers are equal else false
}
public static ArrayList<Integer> armstrongNumbersBetween(int low, int high) {
ArrayList<Integer> numbers = new ArrayList<>();
for(int number = low + 1; number < high; number++) {
if (isArmstrong(number)) {
numbers.add(number);
}
}
return numbers;
}
public static void main(String[] args) {
int low = 999, high = 99999;
ArrayList<Integer> numbers = armstrongNumbersBetween(low, high);
for(int number : numbers) {
System.out.print(number + " ");
}
}
}
Here, each method does exactly one thing and this helps in better reusability. You just have to call the required methods to perform a specific operation.
If something is unclear, let me know.
Below is given array for infinite length which has natural numbers as it can be infinite length:
int[] myArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0, 1, 1, 1, 2, 1, 3 ......};
// at place of 10 it'll take 1, 0, at place of 11 it'll take 1, 1, at place of 12 it'll take 1, 2 an so on.....
index 0 = > value 0 = > number 0
index 1 = > value 1 = > number 1
index 2 = > value 2 = > number 2
index 3 = > value 3 = > number 3
index 4 = > value 4 = > number 4
index 5 = > value 5 = > number 5
index 6 = > value 6 = > number 6
index 7 = > value 7 = > number 7
index 8 = > value 8 = > number 8
index 9 = > value 9 = > number 9
index 10 = > value 1 = > number 10
index 11 = > value 0 = > number 10
index 12 = > value 1 = > number 11
index 13 = > value 1 = > number 11
index 14 = > value 1 = > number 12
index 15 = > value 2 = > number 12
....
....
....
for index 9 value should be 9, but at index 10 instead of value as 10 it should be 1 & again at index 11 value should be 0 , then at index 12 value should be 1 and so on...
Suppose for index value 10 we get a result as 1, for value 11 we get a result value as 0.
We have to write our logic to get the value by passing index value, index can be from 0 to 10000000.
We can not directly use array to get the value at specific index, we have to write logic like below:
public int getValue(int index){
int value = 0;
// logic to find the the value
return value;
}
I have tried below approach to get the result for passed index, but it works till two digits numbers i.e. 99. (till index 189). But for three digits & further we have to change the logic.
public static int myMethod(int index){
System.out.println("index : " + index);
if(index <= 9){
return index;
}
boolean even = (index % 2) == 0;
int num = 0 ;
char res = 0;
if(even){
num = index - ((index - 10) / 2);
System.out.println("num " + num);
res = new Integer(num).toString().charAt(0);
}else{
index = index -1;
num = index - ((index - 10) / 2);
System.out.println("num 22 : " + num);
res = new Integer(num).toString().charAt(1);
}
int result = new Integer(res+"");
System.out.println(result);
return result ;
}
This sequence is known as the Champernowne constant.
The basic approach is to work out how the total length of all the 1 digit, 2 digit, 3 digit numbers, etc. Once you know which digit range is appropriate, you can identify the exact number within the range, and then the exact digit within the number.
Full details of an efficient algorithm can be found in this pdf.
I'm going to say we start from 1 instead of 0 to make the below simpler (you can just subtract 1 from the index to include it).
Let's start with some analysis:
There are 9 1-digit numbers (1-9) consuming the first 9*1 = 9*100*1 = 9 positions.
There are 90 2-digit numbers (10-99) consuming the next 90*2 = 9*101*2 = 180 positions.
There are 900 3-digit numbers (100-999) consuming the next 900*3 = 9*102*3 = 2700 positions.
Etc.
As can be seen from the above, the n-digit numbers take up 9*10n-1*n positions.
From here, it's not too hard to convert an index to the corresponding number by:
Looping over each of the above cases (with a simple for loop), subtracting the corresponding number of positions from our index until doing this would give us a negative number.
Divide our index by the number of digits we're currently at to get the offset, then add the first value using that number of digits (which is a multiple of 10) to find the number we're looking for.
To determine the digit we're looking for (if not looking for the whole number), we can take the remainder of our the above division to give us our answer.
For example:
Let's say we need to get the value of index 200 (or 201, if starting from 0).
Excluding the 1-digit numbers gives us 200-9 = 191.
Excluding the 2-digit numbers gives us 191-180 = 11.
Trying to exclude the 3-digit numbers would lead to a negative number, so we know it's a 3-digit number.
Divide 11 by the number of digits (3) to give us 3 (rounded down).
The 3rd (starting from 0) 3-digit number is 100+3 = 103.
Since 11%3 = 2, we're looking for the 2nd digit (starting from 0), so 3 is our answer.
Code:
final int[] POWERS_OF_10 = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
int getValue(int index){
if (index-- == 0) // remove this if-statement to start from 1
return 0;
int digits = 0;
int positions = 0;
while (positions <= index)
{
index -= positions;
digits++;
positions = 9 * digits * POWERS_OF_10[digits-1];
}
int number = index / digits + POWERS_OF_10[digits-1];
int digit = index % digits;
int value = Integer.toString(number).charAt(digit) - '0'; // lazy approach
// int value = number / POWERS_OF_10[digits-digit-1] % 10; // non-lazy approach
return value;
}
This:
for (int i = 0; i < 20; i++)
System.out.print(getValue(i) + ", ");
Will print out:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4,
Live demo.
This function (in JavaScript convert it yourself to Java), gives for an index x the length of the number and the position of the digit under this index.
Eg. For index 15, it wil return {length: 2, pos: 1} because under index 15 there is the 2 of 12, so the index of 2 relative to 12 is 1 and the length of 12 is 2 digits.
index: 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|..
value: 0|1|2|3|4|5|6|7|8|9|1 |0 |1 |1 |1 |2 |..
I guess that you can write the code to grab the right values from the array yourself.
function find(x){
var length = 0;
while(x > 0){
length++;
x = x - (10**(length-1)) * length * 9;
}
return {length: length, pos: (x % length) + length -1};
}
console.log(find(15));
My problem is as follows; for number N, I need to find out what is the largest value I can count to, when each digit can be used N times.
For example if N = 5, the largest value is 12, since at that point the digit 1 has been used 5 times.
My original approach was to simply iterate through all numbers and keep a tally of how many times each digit has been used so far. This is obviously very inefficient when N is large, so am looking for advice on what would be a smarter (and more efficient) way to achieve this.
public class Counter {
private static Hashtable<Integer, Integer> numbers;
public static void main(String[] args){
Counter c = new Counter();
c.run(9);
}
public Counter() {
numbers = new Hashtable<Integer, Integer>();
numbers.put(0, 0);
numbers.put(1, 0);
numbers.put(2, 0);
numbers.put(3, 0);
numbers.put(4, 0);
numbers.put(5, 0);
numbers.put(6, 0);
numbers.put(7, 0);
numbers.put(8, 0);
numbers.put(9, 0);
}
public static void run(int maxRepeat) {
int keeper = 0;
for(int maxFound = 0; maxFound <= maxRepeat; maxFound++) {
keeper++;
for (int i = 0; i < Integer.toString(keeper).length(); i++) {
int a = Integer.toString(keeper).charAt(i);
//here update the tally for appropriate digit and check if max repeats is reached
}
}
System.out.println(keeper);
}
}
For starters, rather than backing your Counter with a Hashtable, use an int[] instead. When you know exactly how many elements your map has to have, and especially when the keys are numbers, an array is perfect.
That being said, I think the most effective speedup is likely to come from better math, not better algorithms. With some experimentation (or it may be obvious), you'll notice that 1 is always the first digit to be used a given number of times. So given N, if you can find which number is the first to use the digit 1 N+1 times, you know your answer is the number right before that. This would let you solve the problem without actually having to count that high.
Now, let's look at how many 1's are used counting up to various numbers. Throughout this post I will use n to designate a number when we are trying to figure out how many 1's are used to count up to a number, whereas capital N designates how many 1's are used to count up to something.
One digit numbers
Starting with the single-digit numbers:
1: 1
2: 1
...
9: 1
Clearly the number of 1's required to count up to a one-digit number is... 1. Well, actually we forgot one:
0: 0
That will be important later. So we should say this: the number of 1's required to count up to a one-digit number X is X > 0 ? 1 : 0. Let's define a mathematical function f(n) that will represent "number of 1's required to count up to n". Then
f(X) = X > 0 ? 1 : 0
Two-digit numbers
For two-digit numbers, there are two types. For numbers of the form 1X,
10: 2
11: 4
12: 5
...
19: 12
You can think of it like this: counting up to 1X requires a number of 1's equal to
f(9) (from counting up to 9) plus
1 (from 10) plus
X (from the first digits of 11-1X inclusive, if X > 0) plus
however many 1's were required to count up to X
Or mathematically,
f(1X) = f(9) + 1 + X + f(X)
Then there are the two-digit numbers higher than 19:
21: 13
31: 14
...
91: 20
The number of 1's required to count to a two-digit number YX with Y > 1 is
f(19) (from counting up to 19) plus
f(9) * (Y - 2) (from the 1's in numbers 20 through (Y-1)9 inclusive - like if Y = 5, I mean the 1's in 20-49, which come from 21, 31, 41) plus
however many 1's were required to count up to X
Or mathematically, for Y > 1,
f(YX) = f(19) + f(9) * (Y - 2) + f(X)
= f(9) + 1 + 9 + f(9) + f(9) * (Y - 2) + f(X)
= 10 + f(9) * Y + f(X)
Three-digit numbers
Once you get into three-digit numbers, you can kind of extend the pattern. For any three-digit number of the form 1YX (and now Y can be anything), the total count of 1's from counting up to that number will be
f(99) (from counting up to 99) plus
1 (from 100) plus
10 * Y + X (from the first digits of 101-1YX inclusive) plus
however many 1's were required to count up to YX in two-digit numbers
so
f(1YX) = f(99) + 1 + YX + f(YX)
Note the parallel to f(1X). Continuing the logic to more digits, the pattern, for numbers which start with 1, is
f(1[m-digits]) = f(10^m - 1) + 1 + [m-digits] + f([m-digits])
with [m-digits] representing a sequence of digits of length m.
Now, for three-digit numbers ZYX that don't start with 1, i.e. Z > 1, the number of 1's required to count up to them is
f(199) (from counting up to 199) plus
f(99) * (Z - 2) (from the 1's in 200-(Z-1)99 inclusive) plus
however many 1's were required to count up to YX
so
f(ZYX) = f(199) + f(99) * (Z - 2) + f(YX)
= f(99) + 1 + 99 + f(99) + f(99) * (Z - 2) + f(YX)
= 100 + f(99) * Z + f(YX)
And the pattern for numbers that don't start with 1 now seems to be clear:
f(Z[m-digits]) = 10^m + f(10^m - 1) * Z + f([m-digits])
General case
We can combine the last result with the formula for numbers that do start with 1. You should be able to verify that the following formula is equivalent to the appropriate case given above for all digits Z 1-9, and that it does the right thing when Z == 0:
f(Z[m-digits]) = f(10^m - 1) * Z + f([m-digits])
+ (Z > 1) ? 10^m : Z * ([m-digits] + 1)
And for numbers of the form 10^m - 1, like 99, 999, etc. you can directly evaluate the function:
f(10^m - 1) = m * 10^(m-1)
because the digit 1 is going to be used 10^(m-1) times in each of the m digits - for example, when counting up to 999, there will be 100 1's used in the hundreds' place, 100 1's used in the tens' place, and 100 1's used in the ones' place. So this becomes
f(Z[m-digits]) = Z * m * 10^(m-1) + f([m-digits])
+ (Z > 1) ? 10^m : Z * ([m-digits] + 1)
You can tinker with the exact expression, but I think this is pretty close to as good as it gets, for this particular approach anyway. What you have here is a recursion relation that allows you to evaluate f(n), the number of 1's required to count up to n, by stripping off a leading digit at each step. Its time complexity is logarithmic in n.
Implementation
Implementing this function is straightforward given the last formula above. You can technically get away with one base case in the recursion: the empty string, i.e. define f("") to be 0. But it will save you a few calls to also handle single digits as well as numbers of the form 10^m - 1. Here's how I'd do it, omitting a bit of argument validation:
private static Pattern nines = Pattern.compile("9+");
/** Return 10^m for m=0,1,...,18 */
private long pow10(int m) {
// implement with either pow(10, m) or a switch statement
}
public long f(String n) {
int Z = Integer.parseInt(n.substring(0, 1));
int nlen = n.length();
if (nlen == 1) {
return Z > 0 ? 1 : 0;
}
if (nines.matcher(n).matches()) {
return nlen * pow10(nlen - 1);
}
String m_digits = n.substring(1);
int m = nlen - 1;
return Z * m * pow10(m - 1) + f_impl(m_digits)
+ (Z > 1 ? pow10(m) : Z * (Long.parseLong(m_digits) + 1));
}
Inverting
This algorithm solves the inverse of the the question you're asking: that is, it figures out how many times a digit is used counting up to n, whereas you want to know which n you can reach with a given number N of digits (i.e. 1's). So, as I mentioned back in the beginning, you're looking for the first n for which f(n+1) > N.
The most straightforward way to do this is to just start counting up from n = 0 and see when you exceed N.
public long howHigh(long N) {
long n = 0;
while (f(n+1) <= N) { n++; }
return n;
}
But of course that's no better (actually probably worse) than accumulating counts in an array. The whole point of having f is that you don't have to test every number; you can jump up by large intervals until you find an n such that f(n+1) > N, and then narrow down your search using the jumps. A reasonably simple method I'd recommend is exponential search to put an upper bound on the result, followed by a binary search to narrow it down:
public long howHigh(long N) {
long upper = 1;
while (f(upper + 1) <= N) {
upper *= 2;
}
long lower = upper / 2, mid = -1;
while (lower < upper) {
mid = (lower + upper) / 2;
if (f(mid + 1) > N) {
upper = mid;
}
else {
lower = mid + 1;
}
}
return lower;
}
Since the implementation of f from above is O(log(n)) and exponential+binary search is also O(log(n)), the final algorithm should be something like O(log^2(n)), and I think the relation between N and n is linear enough that you could consider it O(log^2(N)) too. If you search in log space and judiciously cache computed values of the function, it might be possible to bring it down to roughly O(log(N)). A variant that might provide a significant speedup is sticking in a round of interpolation search after determining the upper bound, but that's tricky to code properly. Fully optimizing the search algorithm is probably a matter for another question though.
This should be more efficient. Use integer array of size 10 to keep the count of digits.
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
boolean limitReached = false;
while (!limitReached) {
number++;
char[] digits = Integer.toString(number).toCharArray();
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count >= N) {
limitReached = true;
}
}
}
return number;
}
UPDATE 1: As #Modus Tollens mentioned initial code has a bug. When N = 3 it returns 11, but there are four 1s between 1 and 11. The fix is to check if limit is breached count[i] > N on given number, previous number should be return. But if for some i count[i] == N for other j count[j] <= N, the actual number should be returned.
Please see corresponding code below:
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
while (true) {
number++;
char[] digits = Integer.toString(number).toCharArray();
boolean limitReached = false;
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count == N) {
//we should break loop if some count[i] equals to N
limitReached = true;
} else if (count > N) {
//previous number should be returned immediately
//, if current number gives more unique digits than N
return number - 1;
}
}
if (limitReached) {
return number;
}
}
}
UPDATE 2: As #David Z and #Modus Tollens mentioned, in case if N=13, 30 should be returned, ie, algo stops when N is breached but not reached. If this is initial requirement, the code will be even simpler:
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
while (true) {
number++;
char[] digits = Integer.toString(number).toCharArray();
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count > N) {
return number - 1;
}
}
}
}
The example in the book asks the user to enter any positive number. Then the program will add the individual digits separately and print the total. For example if the user enters the number 7512 the program is designed to add 7 + 5 + 1 + 2 and then print the total.
I've written out the way I understand how the code works. Is this correct? Is my understanding of this loop correct with each step, or am I missing any calculations? What happens during the 4th loop when there is no remainder in 7 % 10?
1st run of loop ... sum = sum + 7512 % 10 which is equal to 2
n = 7512 / 10 which which equals to 751
2nd run of loop ... sum = 2 + 751 % 10 which is equal to 1
n = 751 / 10 which is equal to 75
3rd run of loop ... sum = 3 + 75 % 10 which is equal to 5
n = 75 / 10 which is equal to 7
4th run of loop ... sum = 8 + 7 % 10 <------?
import acm.program.*;
public class DigitSum extends ConsoleProgram{
public void run() {
println("This program will add the integers in the number you enter.");
int n = readInt("Enter a positive integer: ");
int sum = 0;
while (n > 0) {
sum += n % 10;
n /= 10;
}
println("The sum of the digits is" + sum + ".");
}
}
The result of the operation 7 % 10 is 7, the remainder when you divide 7 by 10. The last iteration of the loop is to add 7 to the prior value. The next division step inside the loop (n /= 10;) takes n to 0, which is what ends the loop.
% is not the same as /
The % operator is for the modulus, not division... This means that the result of the operations is not dividing, but obtaining the remainder of the division, like:
7512 % 10 => 2
751 % 10 => 1
75 % 10 => 5
7 % 10 => 7
This kind of logic is fairly frequently used when dealing with numeric operations.
before run, sum = 0, n = 7512
1st run of loop ... sum = 0 + 2 => sum = 2, n = 751
2nd run of loop ... sum = 2 + 1 => sum = 3, n = 75
3rd run of loop ... sum = 3 + 5 => sum = 8, n = 7
4th run of loop ... sum = 8 + 7 => sum = 15, n = 0
After 7%10 you get 7 and that is added to your result.
And 7/10 will result in 0 and hence your loop ends and your sum now has addition that you want.