Related
I want to generate the value being searched by the position entered in the check. For example, if 20 is entered, the function should generate numbers starting from 0 and continue in ascending order until 20 digits are created, then output the value of the 20th digit in the generated number string (01234567891011121314), which is 4.
I tried this below, however it is not efficient when it comes to numbers like 1,000,000,000,
[...Array(5).keys()]; output => [0, 1, 2, 3, 4]
Edit this post to clarify I am trying to get a more efficient solution.
Here I am trying to get the answer for long numbers(1,000,000,000) in below one second.
I already have a solution but it takes more than 1 second.
[...Array(5).keys()].join("")[4]; output => 4
This is nearly identical to the Champernowne constant.
A solution from math.stackexchange is:
(Stack Overflow doesn't support MathJax, unfortunately)
The first step is to find what decade you are in. There are 9 digits from the 1 digit numbers, 2⋅90=180 digits from the 2 digit numbers for a total of 189, and generally n⋅9⋅10n−1 from the n digit numbers. Once you have found the decade, you can subtract the digits from the earlier decades. So if you want the 765th digit, the first 189 come from the first and second decades, so we want the 576th digit of the 3 digit numbers. This will come in the ⌈5763⌉=192nd number, which is 291. As 576≡3(mod3), the digit is 1
Programatically:
const getDigit = (target) => {
let i = 0;
let xDigitNumbers = 1; // eg 1 digit numbers, 2 digit numbers
let digitsSoFar = 1;
while (true) {
const digitsThisDecade = xDigitNumbers * 9 * 10 ** (xDigitNumbers - 1);
if (digitsSoFar + digitsThisDecade > target) {
// Then this is the "decade" in which the target digit is
// digitIndexThisDecade: eg, starting from '100101102', to find the last '1' in '101', digitIndexThisDecade will be 6
const digitIndexThisDecade = target - digitsSoFar;
// numIndexThisDecade: this identifies the index of the number in the decade
// eg, starting from '100101102', this could be index 2 to correspond to 101 (one-indexed)
const numIndexThisDecade = Math.floor(digitIndexThisDecade / xDigitNumbers);
// decadeStartNum: the number right before the decade starts (0, 9, 99, 999)
const decadeStartNum = 10 ** (xDigitNumbers - 1);
// num: the number in which the target index lies, eg 101
const num = decadeStartNum + numIndexThisDecade;
// digitIndexInNum: the digit index in num that the target is
// eg, for 101, targeting the last '1' will come from a digitIndexInNum of 2 (zero-indexed)
const digitIndexInNum = digitIndexThisDecade % xDigitNumbers;
return String(num)[digitIndexInNum]
}
digitsSoFar += digitsThisDecade;
xDigitNumbers++;
}
};
for (let i = 0; i < 1000; i++) {
document.write(`${i}: ${getDigit(i)}<br>`);
}
Here's a simple approach without using arrays.
let N = 1000000000, digitsCount = 0, currentNumber = 0;
console.time('Took time: ');
const digits = (x)=>{
if(x<10)
return 1;
if(x<100)
return 2;
if(x<1000)
return 3;
if(x<10000)
return 4;
if(x<100000)
return 5;
if(x<1000000)
return 6;
if(x<10000000)
return 7;
if(x<100000000)
return 8;
if(x<1000000000)
return 9;
return 10; // Default
}
while(true){
digitsCount += digits(currentNumber);
if(digitsCount >= N)
break;
currentNumber++;
}
console.timeEnd('Took time: ');
console.log(String(currentNumber)[N-digitsCount+digits(currentNumber)-1])
Output (The execution time may differ for you but it'll be under 1 second(or 1000ms).)
Took time: : 487.860ms
9
i used .join("") to convert the array to string '01234567891011121314151617181920'
then access the Nth number by Indexing string
N=20;
console.log ( [...Array(N+1).keys()].join("")[N-1] ) //OUTPUT 4
EDIT:i think ther's a solution which is you don't need to create array at all😎
its a mathematical formula
Blockquote
In my Solution , we don't need big iterations and loops...
But This Solution is Big for simple understanding...
I made it for upto 6 digits , and its very efficient...and can be made for any number of digits... And can even be reduced to small functions , but that would get too complex to understand...
So , Total numbers for Given Digits :
For 1 Digit Numbers , They are 10 (0 to 9)....
For 2 Digit Numbers , They are 9*10 => 90 , and total Digits ==> 90*2 ==> 180...
For 3 Digit Numbers , 9*10*10 => 900 , and total Digits ==> 90*3 ==> 2700...
For 4 Digit Numbers , 9*10*10*10 => 9000 , and total Digits ==> 9000*4 ==> 36000...
A function to get Total Digits for a given specified (Number of Digits)
let totalDigits = n => {
if (n == 1) return 10;
return 9 * (10 ** (n - 1)) * n;
}
Now , we set a Range of position for different Digits ,
for 1 Digit , its between 1 and 10....
for 2 Digits , It's Between 11(1+10) and 190(180+10)...(position of 1 in 10 is 11 , and Second 9 in 99 is 190)...
for 3 Digits , It's Between 191(1+10+180) and 2890(2700+180+10)...And so on
for n Digit , Function to get Range is
// This function is used to find Range for Positions... Eg : 2 digit Numbers are upto Position 190...(Position 191 is "100" first digit => 1 )
let digitN = n => {
if (n == 1) return totalDigits(1);
return digitN(n - 1) + totalDigits(n);
}
// To Finally set Ranege for a Given Digit Number... for 1 its [1,10] , for 2 its [11,190]
let positionRange = n => {
if (n == 1) return [1, 10];
else return [digitN(n - 1), digitN(n)]
}
So Final Solution is
// This Function tells the total number of digits for the given digit... Eg : there are 10 one digit Numbers , 180 Two Digit Numbers , 2700 3 Digit Numbers
let totalDigits = n => {
if (n == 1) return 10;
return 9 * (10 ** (n - 1)) * n;
}
// This function is used to find Range for Positions... Eg : 2 digit Numbers are upto Position 190...(Position 191 is "100" first digit => 1 )
let digitN = n => {
if (n == 1) return totalDigits(1);
return digitN(n - 1) + totalDigits(n);
}
// To Finally set Ranege for a Given Digit Number... for 1 its [1,10] , for 2 its [11,190]
let positionRange = n => {
if (n == 1) return [1, 10];
else return [digitN(n - 1), digitN(n)]
}
// A simple Hack to get same value for Different Consecutive Numbers , (0.3 or 0.6 or 0.9 or 1 return 1)
let getDigit = n => {
if (dataType(n) == "float") {
n = Math.floor(n);
n++;
}
return n;
}
// To check for Float or Integer Values
function dataType(x) {
if (Math.round(x) === x) {
return 'integer';
}
return 'float';
}
function f(position) {
let result, charInd, temp;
if ((position >= positionRange(1)[0]) && (position <= positionRange(1)[1])) { // Positions 1 to 10 (1 Digit Numbers)
result = position - 1;
charInd = 0
}
if ((position > positionRange(2)[0]) && (position <= positionRange(2)[1])) { // Positions 11 to 190 (2 Digit Numbers)
temp = (position - 10) / 2;
temp = getDigit(temp);
result = temp + 9;
charInd = (position - 11) % 2
}
if ((position > positionRange(3)[0]) && (position <= positionRange(3)[1])) { // Positions 191 to 2890 (3 Digit Numbers)
temp = (position - 190) / 3;
temp = getDigit(temp);
result = temp + 99;
charInd = (position - 191) % 3
}
if ((position > positionRange(4)[0]) && (position <= positionRange(4)[1])) { // Positions 2891 to 38890 (4 Digit Numbers)
temp = (position - 2890) / 4;
temp = getDigit(temp);
result = temp + 999;
charInd = (position - 2891) % 4
}
if ((position > positionRange(5)[0]) && (position <= positionRange(5)[1])) { // Positions 38890 to 488890 (5 Digit Numbers)
temp = (position - 38890) / 5;
temp = getDigit(temp);
result = temp + 9999;
charInd = (position - 38891) % 5
}
if ((position > positionRange(6)[0]) && (position <= positionRange(6)[1])) { // Positions 488890 to 5888890 (6 Digit Numbers)
temp = (position - 488890) / 6 ;
temp = getDigit(temp);
result = temp + 99999;
charInd = (position - 488891) % 6
}
finalChar = String(result)[charInd];
console.log("Given Position => ", position, " Result Number => ", result, "Char Index ==> ", charInd, "Final Char => ", finalChar);
}
let d1 = Date.now();
f(138971); // Given Position => 138971 Result Number => 30016 Char Index ==> 0 Final Char => 3
let d2 = Date.now();
console.log(d2-d1) ; // 351
I want to generate the value being searched by the position entered in the check. For example, if 20 is entered, the function should generate numbers starting from 0 and continue in ascending order until 20 digits are created, then output the value of the 20th digit in the generated number string (01234567891011121314), which is 4.
I tried this below, however it is not efficient when it comes to numbers like 1,000,000,000,
[...Array(5).keys()]; output => [0, 1, 2, 3, 4]
Edit this post to clarify I am trying to get a more efficient solution.
Here I am trying to get the answer for long numbers(1,000,000,000) in below one second.
I already have a solution but it takes more than 1 second.
[...Array(5).keys()].join("")[4]; output => 4
This is nearly identical to the Champernowne constant.
A solution from math.stackexchange is:
(Stack Overflow doesn't support MathJax, unfortunately)
The first step is to find what decade you are in. There are 9 digits from the 1 digit numbers, 2⋅90=180 digits from the 2 digit numbers for a total of 189, and generally n⋅9⋅10n−1 from the n digit numbers. Once you have found the decade, you can subtract the digits from the earlier decades. So if you want the 765th digit, the first 189 come from the first and second decades, so we want the 576th digit of the 3 digit numbers. This will come in the ⌈5763⌉=192nd number, which is 291. As 576≡3(mod3), the digit is 1
Programatically:
const getDigit = (target) => {
let i = 0;
let xDigitNumbers = 1; // eg 1 digit numbers, 2 digit numbers
let digitsSoFar = 1;
while (true) {
const digitsThisDecade = xDigitNumbers * 9 * 10 ** (xDigitNumbers - 1);
if (digitsSoFar + digitsThisDecade > target) {
// Then this is the "decade" in which the target digit is
// digitIndexThisDecade: eg, starting from '100101102', to find the last '1' in '101', digitIndexThisDecade will be 6
const digitIndexThisDecade = target - digitsSoFar;
// numIndexThisDecade: this identifies the index of the number in the decade
// eg, starting from '100101102', this could be index 2 to correspond to 101 (one-indexed)
const numIndexThisDecade = Math.floor(digitIndexThisDecade / xDigitNumbers);
// decadeStartNum: the number right before the decade starts (0, 9, 99, 999)
const decadeStartNum = 10 ** (xDigitNumbers - 1);
// num: the number in which the target index lies, eg 101
const num = decadeStartNum + numIndexThisDecade;
// digitIndexInNum: the digit index in num that the target is
// eg, for 101, targeting the last '1' will come from a digitIndexInNum of 2 (zero-indexed)
const digitIndexInNum = digitIndexThisDecade % xDigitNumbers;
return String(num)[digitIndexInNum]
}
digitsSoFar += digitsThisDecade;
xDigitNumbers++;
}
};
for (let i = 0; i < 1000; i++) {
document.write(`${i}: ${getDigit(i)}<br>`);
}
Here's a simple approach without using arrays.
let N = 1000000000, digitsCount = 0, currentNumber = 0;
console.time('Took time: ');
const digits = (x)=>{
if(x<10)
return 1;
if(x<100)
return 2;
if(x<1000)
return 3;
if(x<10000)
return 4;
if(x<100000)
return 5;
if(x<1000000)
return 6;
if(x<10000000)
return 7;
if(x<100000000)
return 8;
if(x<1000000000)
return 9;
return 10; // Default
}
while(true){
digitsCount += digits(currentNumber);
if(digitsCount >= N)
break;
currentNumber++;
}
console.timeEnd('Took time: ');
console.log(String(currentNumber)[N-digitsCount+digits(currentNumber)-1])
Output (The execution time may differ for you but it'll be under 1 second(or 1000ms).)
Took time: : 487.860ms
9
i used .join("") to convert the array to string '01234567891011121314151617181920'
then access the Nth number by Indexing string
N=20;
console.log ( [...Array(N+1).keys()].join("")[N-1] ) //OUTPUT 4
EDIT:i think ther's a solution which is you don't need to create array at all😎
its a mathematical formula
Blockquote
In my Solution , we don't need big iterations and loops...
But This Solution is Big for simple understanding...
I made it for upto 6 digits , and its very efficient...and can be made for any number of digits... And can even be reduced to small functions , but that would get too complex to understand...
So , Total numbers for Given Digits :
For 1 Digit Numbers , They are 10 (0 to 9)....
For 2 Digit Numbers , They are 9*10 => 90 , and total Digits ==> 90*2 ==> 180...
For 3 Digit Numbers , 9*10*10 => 900 , and total Digits ==> 90*3 ==> 2700...
For 4 Digit Numbers , 9*10*10*10 => 9000 , and total Digits ==> 9000*4 ==> 36000...
A function to get Total Digits for a given specified (Number of Digits)
let totalDigits = n => {
if (n == 1) return 10;
return 9 * (10 ** (n - 1)) * n;
}
Now , we set a Range of position for different Digits ,
for 1 Digit , its between 1 and 10....
for 2 Digits , It's Between 11(1+10) and 190(180+10)...(position of 1 in 10 is 11 , and Second 9 in 99 is 190)...
for 3 Digits , It's Between 191(1+10+180) and 2890(2700+180+10)...And so on
for n Digit , Function to get Range is
// This function is used to find Range for Positions... Eg : 2 digit Numbers are upto Position 190...(Position 191 is "100" first digit => 1 )
let digitN = n => {
if (n == 1) return totalDigits(1);
return digitN(n - 1) + totalDigits(n);
}
// To Finally set Ranege for a Given Digit Number... for 1 its [1,10] , for 2 its [11,190]
let positionRange = n => {
if (n == 1) return [1, 10];
else return [digitN(n - 1), digitN(n)]
}
So Final Solution is
// This Function tells the total number of digits for the given digit... Eg : there are 10 one digit Numbers , 180 Two Digit Numbers , 2700 3 Digit Numbers
let totalDigits = n => {
if (n == 1) return 10;
return 9 * (10 ** (n - 1)) * n;
}
// This function is used to find Range for Positions... Eg : 2 digit Numbers are upto Position 190...(Position 191 is "100" first digit => 1 )
let digitN = n => {
if (n == 1) return totalDigits(1);
return digitN(n - 1) + totalDigits(n);
}
// To Finally set Ranege for a Given Digit Number... for 1 its [1,10] , for 2 its [11,190]
let positionRange = n => {
if (n == 1) return [1, 10];
else return [digitN(n - 1), digitN(n)]
}
// A simple Hack to get same value for Different Consecutive Numbers , (0.3 or 0.6 or 0.9 or 1 return 1)
let getDigit = n => {
if (dataType(n) == "float") {
n = Math.floor(n);
n++;
}
return n;
}
// To check for Float or Integer Values
function dataType(x) {
if (Math.round(x) === x) {
return 'integer';
}
return 'float';
}
function f(position) {
let result, charInd, temp;
if ((position >= positionRange(1)[0]) && (position <= positionRange(1)[1])) { // Positions 1 to 10 (1 Digit Numbers)
result = position - 1;
charInd = 0
}
if ((position > positionRange(2)[0]) && (position <= positionRange(2)[1])) { // Positions 11 to 190 (2 Digit Numbers)
temp = (position - 10) / 2;
temp = getDigit(temp);
result = temp + 9;
charInd = (position - 11) % 2
}
if ((position > positionRange(3)[0]) && (position <= positionRange(3)[1])) { // Positions 191 to 2890 (3 Digit Numbers)
temp = (position - 190) / 3;
temp = getDigit(temp);
result = temp + 99;
charInd = (position - 191) % 3
}
if ((position > positionRange(4)[0]) && (position <= positionRange(4)[1])) { // Positions 2891 to 38890 (4 Digit Numbers)
temp = (position - 2890) / 4;
temp = getDigit(temp);
result = temp + 999;
charInd = (position - 2891) % 4
}
if ((position > positionRange(5)[0]) && (position <= positionRange(5)[1])) { // Positions 38890 to 488890 (5 Digit Numbers)
temp = (position - 38890) / 5;
temp = getDigit(temp);
result = temp + 9999;
charInd = (position - 38891) % 5
}
if ((position > positionRange(6)[0]) && (position <= positionRange(6)[1])) { // Positions 488890 to 5888890 (6 Digit Numbers)
temp = (position - 488890) / 6 ;
temp = getDigit(temp);
result = temp + 99999;
charInd = (position - 488891) % 6
}
finalChar = String(result)[charInd];
console.log("Given Position => ", position, " Result Number => ", result, "Char Index ==> ", charInd, "Final Char => ", finalChar);
}
let d1 = Date.now();
f(138971); // Given Position => 138971 Result Number => 30016 Char Index ==> 0 Final Char => 3
let d2 = Date.now();
console.log(d2-d1) ; // 351
What I would like to do:
some how create a variable that represents the binary system more accurately because I have only included the first 12 numbers, but what if I need many of the higher numbers! It needs to be more generic.
What I have tried to do: push the binary numbers that go into n to a new array called one, then return the length of the array as the answer.
var countBits = function(n) {
Let binary = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048];
var one = [];
for( let i = 0; i < binary.length; i++) {
if( n = [i]) { one.push([i])},
if( n > [i]) { one.push([i])},
else { return "negative"};
return one.length;
};
Syntactic problem
Following my comment, here is the correction:
let instead of Let
remove the , on both if statements
a missing }
I would recommend you use some kind of editor to help tidy and validate your code.
It will save you a lot of time!
var countBits = function(n) {
let binary = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048];
var one = [];
for (let i = 0; i < binary.length; i++) {
if (n == [i]) {
one.push([i])
}
if (n > [i]) {
one.push([i])
} else {
return "negative"
}
}
return one.length;
};
Solving the actual bit-counter problem
Now onto the problem, you would like to:
count the number of set bits on the binary representation of a given number
We will run our loop as long as n is not positive or equal to zero. On each loop, we will check if n is odd in which case we will add 1 to our counter. Then we will divide n by 2.
const countBits = function(n) {
let count = 0;
while (n > 0) {
count += (n % 2 != 0); // add one to the counter if n is pair
n = Math.floor(n/2); // divide n by 2
}
return count;
}
You can do the exact same using binary operators: & (bitwise AND) and >> (right-shift). We will be working on n directly and counting down to determine the number of set bits.
Here are our two lines, now using & and >>:
On each loop, we will compare n with 1 bit-wise. Essentially n & 1 will equal to 1 if n is dividable by 2, else it will equal to 0.
Then we will shift n by one, i.e. move all bits to the right. n >>= 1 is the same as assigning the result of n >> 1 to n: n = n >> 1.
const countBits = function(n) {
let count = 0;
while (n > 0) {
count += n & 1;
n >>= 1;
}
return count;
}
More on bitwise operators.
One-liner
If you want to test your code out, here's a cheated version to compare your results to:
const countBits = function(dec) {
return (dec >>> 0).toString(2).split('').filter(Number).length;
}
It first converts the number to a string of its binary representation, then converts it to an array of 0s and 1s, remove the zeros with a filter, then counts the number of remaining elements.
I have to make a URL shortener for query strings. Have spent few days trying to compress array data into base64 strings. Thinking that the best approach may be to interpret something like "[[1,2,9,3],[1,0,2],[39,4]]" as base13 with numbers 0-9 and [], symbols.
how the current algorithm works:
convert the stringified arrays into an array of base13, where each element represents 1 unique character, convert this array to base10 number, convert this number to base 64 string.
but the problem is when converting the base13 array to base10 number, it makes large numbers like 5.304781188371057e+86 which cant be held in js.
I am open to alternative solutions of course, but please do not suggest something like creating a database of URLs as it won't work as I have up to 51!*51! unique URLs, better to just make a compact encodable and decodable query string and decode it as soon as the website is accessed.
//convert stringified array to array of base13(each element = each digit of base13 number)
function stringToArray(string)
{
let charSet = "[],1234567890";
let array = [];
for(let i = 0; i < string.length; i++)
{
array.push(charSet.indexOf(string[i]));
}
return array;
}
//convert base13 array to one large decimal number
function arrayToDecimal(array, base)
{
var decimal = 0;
for(let i = 0; i < array.length; i++)
{
decimal += array[i] * Math.pow(base, i)
}
return decimal;
}
//convert decimal number back to array
function decimalToArray(decimal, base)
{
var quotient = decimal;
var remainder = [];
while(quotient > base)
{
remainder.push(quotient % base)
quotient = Math.floor(quotient / base);
}
remainder.push(quotient % base)
return remainder;
}
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
// binary to string lookup table
const b2s = alphabet.split('');
// string to binary lookup table
// 123 == 'z'.charCodeAt(0) + 1
const s2b = new Array(123);
for(let i = 0; i < alphabet.length; i++)
{
s2b[alphabet.charCodeAt(i)] = i;
}
// number to base64
const ntob = (number) =>
{
if(number < 0) return `-${ntob(-number)}`;
let lo = number >>> 0;
let hi = (number / 4294967296) >>> 0;
let right = '';
while(hi > 0)
{
right = b2s[0x3f & lo] + right;
lo >>>= 6;
lo |= (0x3f & hi) << 26;
hi >>>= 6;
}
let left = '';
do {
left = b2s[0x3f & lo] + left;
lo >>>= 6;
} while(lo > 0);
return left + right;
};
// base64 to number
const bton = (base64) =>
{
let number = 0;
const sign = base64.charAt(0) === '-' ? 1 : 0;
for(let i = sign; i < base64.length; i++)
{
number = number * 64 + s2b[base64.charCodeAt(i)];
}
return sign ? -number : number;
};
console.log(decimalToArray(bton(ntob(arrayToDecimal([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 13))), 13))
//encoded and decoded, works output:[1,1,1,1,1,1,1,1,1,1,1,1,1]
console.log(arrayToDecimal([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 13))
//encoding doesnt work, array to decimal converts to 5.304781188371057e+86```
An interesting problem... The first thing you will need to assess is whether the base conversion compression you're seeking is worthwhile. Ie, how many base 64 characters are required to represent n characters of base 13? This involves solving...
13 ** n = 64 ** x
Solving for x, we get...
x = n * log(13) / log(64)
Ie, for every n digits of base 13, how many digits of base 64 are required. A sampling of a few values of n returns...
n = 6, x = 3.70
n = 7, x = 4.31
n = 8, x = 4.93
n = 9, x = 5.55
n = 10, x = 6.17
n = 11, x = 6.78
n = 12, x = 7.40
n = 13, x = 8.01
n = 14, x = 8.63
n = 15, x = 9.25
n = 16, x = 9.86
So how to interpret this? If you have 10 digits of base 13, you're going to need 7 digits (6.17 rounded up) of base 64. So the best ratio is when x is equal to, or just under, a whole number. So, 8 digits of base 13 requires 5 digits of base 64, achieving a best case of 5/8 or 62.5% compression ratio.
Assuming that's good enough to meet your requirement, then the following function converts the "base13" string to base 64.
const base13Chars = "0123456789[],";
const base64Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_';
// see https://en.wikipedia.org/wiki/Query_string for URL parameter allowable characters.
function base13toBase64(x13) {
base13 = x13.split("").map( c => base13Chars.indexOf(c) );
// Make the array an even multiple of 8
for (i = base13.length; i % 8 !==0; i++) {
base13[i] = 0;
}
x64 = "";
for (i = 0; i < base13.length; i += 8) {
// Calculate base13 value of the next 8 characters.
let n = 0;
for (j = 0; j < 8; j++) {
n = n * 13 + base13[i + j];
}
// Now calculate the base64 of n.
for (j = 0; j < 5; j++) {
x64 = x64 + base64Chars.substr(n % 64,1);
n = Math.floor(n / 64);
}
}
return x64;
}
Running the above...
base13toBase64( "[[1,2,9,3],[1,0,2],[39,4]]" ) returns "ilYKerYlgEJ4PxAAjaJi"
Note that the original value is a length of 26 characters, and the base64 value is 20 characters, so the compression ratio is 77%, not quite the optimal 62.5%. This is because of the padding to bring the original array to 32 characters, an even multiple of 8. The longer the string to encode, though, the closer the ratio will be to 62.5%.
Then, on the server side you'll need the constants above plus the following function to "uncompress" the base64 to the base13 stringified URL...
function base64toBase13(x64) {
base64 = x64.split("").map( c => base64Chars.indexOf(c) );
x13 = "";
for (i = 0; i < base64.length; i += 5) {
// Calculate base64 value of the next 5 characters.
let n = 0;
for (j = 5 - 1; 0 <= j; j--) {
n = n * 64 + base64[i + j];
}
// Now calculate the base13 of n.
let x = "";
for (j = 0; j < 8; j++) {
x = base13Chars.substr(n % 13,1) + x;
n = Math.floor(n / 13);
}
x13 = x13 + x;
}
// Removed the trailing 0's as a result of the buffering in
// base13toBase64 to make the array an even multiple of 8.
while (x13.substr(-1,1) === "0") {
x13 = x13.substr(0, x13.length - 1);
}
return x13;
}
Running the above...
base64toBase13 ( "ilYKerYlgEJ4PxAAjaJi" ) returns "[[1,2,9,3],[1,0,2],[39,4]]"
Hope this helps...
The best compression is when you can leave stuff out.
assuming your data structure is Array<Array<int>> given by the one sample, we can leave out pretty much everything that doesn't contribute to the data itself.
I'm not compressing the string, but the data itself with 1 b64Character / 5 bits needed to represent a number. as for the structure we only store the number of sub-arrays and their respective lengths; so more or less an additional character per Array in your data.
boils down to:
function encode(data) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let str = "";
function encode(nr, hasMoreDigits) {
if (nr > 31) {
// I need more bits/characters to encode this number.
//encode the more significant bits with the 0b100000 flag
encode(nr >>> 5, 32);
}
// 0b011111 payload | 0b100000 flag
const index = nr & 31 | hasMoreDigits;
str += alphabet[index];
}
encode(data.length);
data.forEach(arr => {
encode(arr.length);
arr.forEach(v => encode(v >>> 0 /* int32 -> uint32 */));
});
return str;
}
function decode(str) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let i = 0;
function parse() {
let nr = 0,
hasMoreDigits;
do {
const index = alphabet.indexOf(str.charAt(i++));
nr = nr << 5 | index & 31; // 0b011111 payload
hasMoreDigits = index & 32; // 0b100000 flag
} while (hasMoreDigits);
return nr; // int32 due to the bit operations above
}
let data = Array(parse());
for (let j = 0; j < data.length; ++j) {
let arr = data[j] = Array(parse());
for (let k = 0; k < arr.length; ++k) {
arr[k] = parse();
}
}
return data;
}
let data = [
[1, 2, 9, 3],
[1, 0, 2],
[39, 4]
];
let text = encode(data);
let data2 = decode(text);
console.log("input:", data);
console.log("encoded:", text, "length:", text.length);
console.log("output:", data2);
console.log("equal:", JSON.stringify(data) === JSON.stringify(data2));
.as-console-wrapper{top:0;max-height:100%!important}
The encoding of the numbers. Ideally you would encode a number as binary with a static size, but this means 32bit/int which would be 6characters/number, so multibytes.
We split the number into chunks of 'n' bits, ignore the leading zeroes and encode the rest. ideally we can encode small number with very few characters, downside: we loose 1bit/chunk if n is too small and the average numbers are big. It's a tradeoff; that's why I left this configurable.
the current format is 6bits/number. 1 for the Structure, 5 bits as payload. In the format (1.....)*0.....
I would suggest you directly encode the Base13 string into Base64.
Although that might not result in better compression than your solution, it removes the heavy multiplications you are performing. Moreover how do you guarantee that no collisions happen when converting through arrayToDecimal ?
https://leetcode.com/problems/numbers-with-repeated-digits/
Given a positive integer N, return the number of positive integers less than or equal to N that have at least 1 repeated digit.
Example 1:
Input: 20
Output: 1
Explanation: The only positive number (<= 20) with at least 1 repeated digit is 11.
Example 2:
Input: 100
Output: 10
Explanation: The positive numbers (<= 100) with atleast 1 repeated digit are 11, 22, 33, 44, 55, 66, 77, 88, 99, and 100.
Example 3:
Input: 1000
Output: 262
Note:
1 <= N <= 10^9
https://leetcode.com/problems/numbers-with-repeated-digits/discuss/256725/JavaPython-Count-the-Number-Without-Repeated-Digit
I found out this solution has many likes. but it's coded in Java/Python. Anyone can help to use Javascript to code it by the similar logic.
Really appreciate...
python solution: I have no clue how the set() part, how to use Javascript to do it.
def numDupDigitsAtMostN(self, N):
L = map(int, str(N + 1))
res, n = 0, len(L)
def A(m, n):
return 1 if n == 0 else A(m, n - 1) * (m - n + 1)
for i in range(1, n): res += 9 * A(9, i - 1)
s = set()
for i, x in enumerate(L):
for y in range(0 if i else 1, x):
if y not in s:
res += A(9 - i, n - i - 1)
if x in s: break
s.add(x)
return N - res
While I can't translate the python, I think what you need to do is break the integer to array of char, so 100 would become ["1","0","0"], and do a comparison from the 1st element (array[0]) to the last element to see if any of the char is the same.
Here is a quick function that I do.. maybe it will run slower than the python, but it should do the job:
var result = [];
function x(n) {
for (var i = 0; i <= n; i++){
var nStr = i.toString();
var nStrArr = nStr.split('');
if(nStrArr.length > 1){
for(var j = 0; j < nStrArr.length; j++){
var idx = nStrArr.findIndex(x => x === nStrArr[j]);
if(idx >= 0 && idx !== j){
result.push(parseInt(i));
break;
}
}
}
}
console.log(result); //should list all the numbers with at least 1 repeated digit
console.log(result.length); //length of the array
}
x(1000);