如何在Javascript中将十进制(Base 10)转换为分数基数(例如Base 3/2)

3
我发现很多资源都讨论了进制转换算法和使用内置的JavaScript函数将十进制转换为二进制或十六进制,但我找不到任何关于如何转换为任何分数基数(例如3/2进制)的资源。Sesquinary数字系统非常有趣,我想能够使用它,但我找不到任何从十进制转换为它的方法。
sesquinary系统如下:1、2、20、21、22、210、211、212、2100、2101等。
我找不到可靠的转换方法。以下是我到目前为止编写的代码:
function decimalToBase(number, base) {
    let digitArray = [];
    let quotient;
    let remainder;

    for (let i = 0; i < 16; i++) {
        quotient = Math.floor(number / base);
        remainder = number % base;
        digitArray.unshift(remainder);
        number = quotient;

        if (quotient == 0){
            break;
        }
    }
    console.log(digitArray)
}

它适用于小于10的整数进制,但如果我输入一个分数进制,比如3/2,那么我得到的结果将带有小数:
[1, 0.5, 1, 0] // When It should be: [2, 1, 0]

任何帮助都将不胜感激。


首先,你不能在非整数基数上使用 % - user5734311
@ChrisG 我应该使用什么替代品? - Wowkster
@ChrisG 我不明白为什么会有问题。 - Wowkster
嗯...通常在一个基数为n的数字系统中,数字范围从0...n-1。例如,基数为4的数字系统具有数字0、1、2和3。看起来,在建议的基数为3/2的sesquinary系统中,允许使用数字2?什么是逻辑,使得基数为3/2的系统可以使用数字0、1和2 - Trentium
@JonTrent 这实际上是一个常见的误解。你可能会认为基数的值是符号的数量,但这实际上只是巧合。否则,分数基将是不可能的,但它们在数学上与整数基一样可靠。据我所知,符号的数量是分子的值(在整数基中,分子只是基数的值),因为拥有3/2个数字是不可能的。 - Wowkster
没关系,好像它运行良好。 - user5734311
2个回答

2

使用以下资源...

...提出的解决方案是...

<html><body>
BaseHi: <input id='hi' value='3' /><br>
BaseLo: <input id='lo' value='2' /><br>
Value:  <input id='val' value='6' /><br>
<button onclick='go()'>Go!</button><br>
<br>
<br>
Result: <span id='result'></span>
<br><br>
Check: <span id='check'></span>

<script>

function decimalToBase( number, baseNum, baseDen ) {

  let digitArray = []
  digitArray[ 0 ] = number;
  let i = 0;
  
  while ( digitArray[ i ] && baseDen < digitArray[ i ] ) {
    let qi = Math.trunc( digitArray[ i ] / baseNum );
    let ri = digitArray[ i ] - qi * baseNum;
    digitArray[ i ] = ri;
    digitArray[ i + 1 ] = ( digitArray[ i + 1 ] || 0 ) + qi * baseDen;
    i++;
  }
  
  digitArray.reverse();
  return digitArray;
  
}

function go() {
  let baseHi = parseInt( document.getElementById( 'hi' ).value );
  let baseLo = parseInt( document.getElementById( 'lo' ).value );
  let value = parseInt( document.getElementById( 'val' ).value );
  if ( baseHi <= baseLo ) {
    document.getElementById( 'result' ).innerHTML = 'Error - BaseHi must be greater than BaseLo';
    return;
  }
  let result = decimalToBase( value, baseHi, baseLo );
  document.getElementById( 'result' ).innerHTML = `${value}<sub>10</sub> = ${result.join( '.' )}<sub>${baseHi}/${baseLo}</sub>`;
  
  let check = '';
  let total = 0;
  for ( let i = result.length - 1, bd = 1; 0 <= i ; i--, bd = bd * baseHi / baseLo ) {
    total += bd * result[ i ];
    check = `${result[i]}*(${baseHi}/${baseLo})<sup>${result.length - i - 1}</sup>` + ( check ? ' + ' : '' ) + check;
  }
  document.getElementById( 'check' ).innerHTML = `${check} = ${total}<sub>10</sub>`;
}
</script>
</body></html>

编辑: 调整答案以便于测试各种进制和值,包括反向计算新基数值返回到基数10的交叉检查。令人惊讶的是,该算法似乎适用于BaseLo < BaseHi的情况。

享受吧!


2
对于特定的3/2情况,James Propp在这篇文章中提供了几种方法。其中一种由Simon Norton在《End Notes》中描述:

这基于以下事实:当n被写成3k + r的形式时,余数r为0、1或2,n的sesquinary表示等于2k的sesquinary表示,数字r附加在末尾。这使我们可以通过反复除以3、向下取整和双倍的过程快速从右到左编写n的sesquinary表示。

阅读全文

他们还提供了一些Mathematica代码来运行。

S[n_] := If[n < 3, {n}, Append[S[2 Floor[n/3]], Mod[n, 3]]]

我不太了解Mathematica,所以无法逐一将其重写为js代码,但如果我理解算法正确的话,大概应该是这样的:

const decimalToSesquinary = (n) => {
  const res = [];
  while (n) {
    const k = Math.floor(n / 3);
    res.push(n % 3);
    n = k * 2;
  }
  return res.reverse().join("");
};

console.log(decimalToSesquinary(6)) // expected "210"
console.log(decimalToSesquinary(7)) // expected "211"
console.log(decimalToSesquinary(8)) // expected "212"
console.log(decimalToSesquinary(100)) // expected "212001201"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接