Decibinary Numbers
Decibinary Numbers
+ 6 comments I want to provide a hint for people who are still trying to wrap their head around the problem. One advanced book that is on topic is, "Combinatorial Algorithms", by Kreher and Stinson.
My breakthrough happened when I printed out a few tables. Given a function that converts decibinary to its decimal value, the 1 digit numbers have their obvious decimal values. I recommend printing tables of decibinary numbers in groups of 10, then you will start seeing a pattern.
In python, this might look like,
>>> [ db_value(x) for x in range(0,10) ] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> [ db_value(x) for x in range(10,20) ] [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] >>> [ db_value(x) for x in range(20,30) ] [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] >>> [ db_value(x) for x in range(90,100) ] [18, 19, 20, 21, 22, 23, 24, 25, 26, 27] >>> [ db_value(x) for x in range(100,110) ] [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
+ 2 comments C++ solution which is logically the same as the one posted previously but hopefully a little easier to understand.
const int dmax = 300000; const int digits = 10; const int powers = 20; vector<vector<long>> v(dmax, vector<long>(powers)); vector<long> c(dmax); // Complete the decibinaryNumbers function below. void preCompute() { // Compute the number of duplicates for each value, number of digits. for (int i = 0; i < dmax; i++) { v[i][0] = i < digits; for (int j = 1; j < powers; j++) { // Duplicates is sum of all shorter numbers duplicates for each digit. for (int k = 0; k < digits; k++) { int value = i - k * (1 << j); // Exit if using digit creates number larger than target value. if (value < 0) break; v[i][j] += v[value][j - 1]; } } } // Calculate the absolute offset for the first duplicate of each number. for (int i = 1; i < dmax; i++) { c[i] = v[i - 1][powers - 1] + c[i - 1]; } } long decibinaryNumbers(long x) { long result = 0; auto l = upper_bound(c.begin(), c.end(), x - 1) - 1; int value = l - c.begin(); long offset = (x - 1) - *l; // Find each digit. for (int i = powers - 1; i >= 1; i--) { int power = 1 << i; // Find the digit which takes us closest to offset. for (int digit = 0; digit < digits; digit++) { // Calculate value of remaining digits. int v1 = value - digit * power; // If index is less than duplicates for remainder we have our digit. if (offset < v[v1][i - 1]) { result += digit; value -= power * digit; break; } // Subtract number of duplicates for this digit from offset. offset -= v[v1][i - 1]; } result *= 10; } // Whatever is left must be the last digit. result += value; // cout << x << ":" << result << ":" << l - c.begin() << endl; return result; }
+ 0 comments Example tests:
2 1104 1963
result:
11111 (31 in decibinary) 10406 (38 written in decibinary)
+ 2 comments
+ 1 comment Can anyone help suggest a further optimization for the following solution? (I've implemented it in python; it passes 6/11 tests and times out the rest).
1) Figure out the number of decibinary numbers corresponding to each decimal number using the following dynamic programming algorithm suggested by xinwzh:
- where
g[dec][dig]
gives the number of decibinary numbers equal in value to decimal {dec} with at most {dig} digits, you can getg[dec][dig]
from:g[dec][dig - 1] + g[dec - offset][dig - 1] + g[dec - 2 x offset][dig - 1] + g[dec - 3 x offset][dig - 1] + ... + g[0][dig - 1]
, whereoffset = 2^(dig - 1)
. - Total number of decibinary numbers equal to a given decimal number is retrieved by:
g[dec][length of g[dec] - 1]
2) Add up the number of decibinary numbers corresponding to each decimal from
0, 1, 2, 3, etc.
until the total is greater than the query.- The decimal that put it over the list is the decimal corresponding to the queried decibinary.
- The queried decibinary will be the nth decibinary of that decimal, where n is the difference between the query and the previous total (i.e. all the decibinaries up to but not including the decimal corresponding to the queried decibinary).
3) Find the queried decibinary by either a) finding the first decibinary and cycling up
n
times, or b) finding the last and cycling downg[dec][len(gs[dec]) -1] - n
times.- Find the next highest decibinary corresponding to the same decimal number by incrementing up the digits and looking for the first pair of digits where you can subtract two from the lower digit and add one to the higher digit. i.e., 200 -> 1000, 54 -> 62. However, you also need to go back down the digits and maximize in the other direction: 1900 -/-> 2700, 1900 -> 2188.
- Find the next lowest decibinary by the same thing, but reversed
Does anyone see where I might be able to optimize something further?? Is it possible to find the nth decibinary number equal to a given decimal number without cycling through the way I'm doing? (This is the bottleneck.)
- where
Sort 41 Discussions, By:
Please Login in order to post a comment