考虑"数字字母表"的概念;"数字字母表"是一个String
,其中String
中的每个字符代表一个可用于表示数字的有效数字.String
的总长度代表该特定"数字字母表"的基数.字符在String
中的顺序表示每个字符的连续值.一个字符不能在"数字字母表"中重复.
例如,String
'0123456789'
代表典型的十进制数;任何字符都在'0123456789'
内的String
都能够被解释为十进制值.具体地说,我们应该能够说interpret('0123456789', '127') === 127
.此interpret
函数接受"数字字母表"和该"数字字母表"中的值,并返回数值.
实施interpret
条并不难:
const interpret = (alphabet, str) => {
let sum = 0;
const base = alphabet.length;
const maxInd = str.length - 1;
for (let i = 0; i <= maxInd; i++) {
// Simple to get value of char within alphabet - I know this is O(m * n) but not
// concerned about that here
const chrVal = alphabet.indexOf(str[i]);
// Earlier characters count for more, same as how in the number 4278 the 4 represents
// 4000, larger than the 2 which is 200, larger than the 7 which is 70, etc.
const placeInd = maxInd - i;
// The "place" value in `str`, same as how in the number 4278 the 4 is 1000s, 2 is
// 100s, 7 is 10s, etc.
const placeVal = base ** placeInd;
// Overall the value contributed by this "place" in `str` is `placeVal` times the
// value of the character in `str` at this place
sum += placeVal * chrVal;
}
return sum;
};
const tests = [
{ alpha: '0123456789', str: '20' },
{ alpha: '0123456789', str: '119992' },
{ alpha: '0123456789', str: '0000119992' },
{ alpha: '0123456789abcdef', str: 'ff' },
{ alpha: '0123456789abcdef', str: 'beef' },
{ alpha: '0123456789abcdef', str: 'deadbeef' },
{ alpha: '0123456789abcdef', str: 'a0' },
{ alpha: '01', str: '100111100' },
{ alpha: '-+', str: '+--++++--' },
{ alpha: '~!@#', str: '@#!#' }, // Now there's no reason we can't do this
];
for (const test of tests) console.log(`interpret('${test.alpha}', '${test.str}') = ${interpret(test.alpha, test.str)}`);
然而,我的主要目标是将字符串从任意字母表转换为其他任意字母表;我将该任务称为100,以下是一个用法示例:
reinterpret('0123456789', '01', '1337') === '10100111001'
在这种情况下,"0123456789"作为源字母表,"01"作为目标字母表;字符串'1337'
表示源字母表中的1337
,并且reinterpret
返回'10100111001'
,表示目标字母表中的相同值.
你可能认为实现reinterpret
很容易,因为我们可以很容易地编写一个函数来反转interpret
(称之为uninterpret
),然后简单地实现为:
reinterpret = (srcAlpha, trgAlpha, str) => uninterpret(trgAlpha, interpret(srcAlpha, str));
不过,这其中存在一个问题.下面是一个uninterpret
实现;请注意,它颠倒了interpret
的结果:
const logBaseHandleLikelyIntegers = (base, num) => {
const eps = 0.000000000000005;
const rough = Math.log(num) / Math.log(base); // May have a floating point error
// Try to correct floating point error when it looks like it should be an integer
if (Math.floor(rough + eps) > rough) return Math.floor(rough) + 1;
if (Math.ceil( rough - eps) < rough) return Math.floor(rough) - 1;
return rough;
};
const uninterpret = (alpha, num) => {
const base = alpha.length;
// Use log where the base is the alphabet length to determine number of chars needed
const digitsNeeded = Math.floor(logBaseHandleLikelyIntegers(base, num) + 1);
let str = '';
for (let p = digitsNeeded - 1; p >= 0; p--) {
const pow = Math.pow(base, p);
const div = Math.floor(num / pow);
str += alpha[div];
num -= pow * div;
}
return str;
};
const tests = [
{ alpha: '0123456789', num: 20 },
{ alpha: '0123456789', num: 119992 },
{ alpha: '0123456789abcdef', num: 255 },
{ alpha: '0123456789abcdef', num: 48879 },
{ alpha: '0123456789abcdef', num: 3735928559 },
{ alpha: '0123456789abcdef', num: 160 },
{ alpha: '01', num: 316 },
{ alpha: '-+', num: 316 },
{ alpha: '~!@#', num: 183 }, // Now there's no reason we can't do this
{ alpha: '0123456789', num: 1000 }, // Cases like this require an integer log result and necessitate the helper function
];
for (const test of tests) console.log(`uninterpret('${test.alpha}', '${test.num}') = ${uninterpret(test.alpha, test.num)}`);
下面是使用interpret
和uninterpret
的reinterpret
实现:
const logBaseHandleLikelyIntegers = (base, num) => {
const eps = 0.000000000000005;
const rough = Math.log(num) / Math.log(base); // May have a floating point error
// Try to correct floating point error when it looks like it should be an integer
if (Math.floor(rough + eps) > rough) return Math.floor(rough) + 1;
if (Math.ceil( rough - eps) < rough) return Math.floor(rough) - 1;
return rough;
};
const interpret = (alphabet, str) => {
let sum = 0;
const base = alphabet.length;
const maxInd = str.length - 1;
for (let i = 0; i <= maxInd; i++) {
// Simple to get value of char within alphabet - I know this is O(m * n) but not
// concerned about that here
const chrVal = alphabet.indexOf(str[i]);
// Earlier characters count for more, same as how in the number 4278 the 4 represents
// 4000, larger than the 2 which is 200, larger than the 7 which is 70, etc.
const placeInd = maxInd - i;
// The "place" value in `str`, same as how in the number 4278 the 4 is 1000s, 2 is
// 100s, 7 is 10s, etc.
const placeVal = base ** placeInd;
// Overall the value contributed by this "place" in `str` is `placeVal` times the
// value of the character in `str` at this place
sum += placeVal * chrVal;
}
return sum;
};
const uninterpret = (alpha, num) => {
const base = alpha.length;
// Use log where the base is the alphabet length to determine number of chars needed
const digitsNeeded = Math.floor(logBaseHandleLikelyIntegers(base, num) + 1);
let str = '';
for (let p = digitsNeeded - 1; p >= 0; p--) {
const pow = Math.pow(base, p);
const div = Math.floor(num / pow);
str += alpha[div];
num -= pow * div;
}
return str;
};
const reinterpret = (srcAlpha, trgAlpha, str) => uninterpret(trgAlpha, interpret(srcAlpha, str));
const reinterpretTest = (src, trg, str) => {
console.log(`Reinterpret the value "${str}" in alphabet "${src}", under alphabet "${trg}":`);
console.log(`Result: ${reinterpret(src, trg, str)}`);
};
console.log('Base 10 -> base 2 test:');
reinterpretTest('0123456789', '01', '32');
console.log('\nBase 10 -> base 16 tests:');
reinterpretTest('0123456789', '0123456789abcdef', '1287589');
reinterpretTest('0123456789', '0123456789abcdef', '87771287589933381');
reinterpretTest('0123456789', '0123456789abcdef', '87771287589933382');
运行上面的代码,然后...啊哦,两个不同的字符串'87771287589933381'
和'87771287589933382'
被解释为相同的值'137d3856246d140'
.为什么?因为这些字符串很大,而uninterpret
中的数字变得如此之大,以至于失go 了精度.
我认为这很棘手;我认为应该以某种方式避免求幂.
我根本不是在问Number
的最大大小/精度;我是想绕过当前代码中由于该最大精度而存在的限制.