考虑"数字字母表"的概念;"数字字母表"是一个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)}`);

下面是使用interpretuninterpretreinterpret实现:

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的最大大小/精度;我是想绕过当前代码中由于该最大精度而存在的限制.

推荐答案

BigInt

const interpret = (alphabet, str) => {
  
  let sum = 0n;
  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 = BigInt(base) ** BigInt(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 * BigInt(chrVal);
    
  }
  return sum;
  
};
const uninterpret = (alpha, num) => {
  
  const base = BigInt(alpha.length);
  const digits = [];

  while (num) {
    digits.push(alpha[num % base]);
    num /= base;
  }
  
  return digits.reverse().join("");
  
};
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');

注:uninterpret(alpha, 0n) === ""

Javascript相关问答推荐

主要内部的ExtJS多个子应用程序

如何判断属于多个元素的属性是否具有多个值之一

单击更新页面的按钮后,页面刷新;测试/断言超时,有两个标题,但没有一个标题

从mat—country—select获取整个Country数组

如何解决CORS政策的问题

构造HTML表单以使用表单数据创建对象数组

在Three JS中看不到补间不透明度更改效果

还原器未正确更新状态

如何在每次单击按钮时重新加载HighChart/设置HighChart动画?

在Java中寻找三次Bezier曲线上的点及其Angular

如何在JAVASCRIPT中临时删除eventListener?

Vaadin定制组件-保持对javascrip变量的访问

在不删除代码的情况下禁用Java弹出功能WordPress

回溯替代方式

在Odoo中如何以编程方式在POS中添加产品

Phaser3 preFX addGlow不支持zoom

如何在AG-Grid文本字段中创建占位符

React Refs不与高阶组件(HOC)中的动态生成组件一起工作

使用createBrowserRoutVS BrowserRouter的Reaction路由

使用Java脚本替换字符串中的小文本格式hashtag