代码之家  ›  专栏  ›  技术社区  ›  Roja Buck

一种将10进制数转换为N进制数的算法

  •  10
  • Roja Buck  · 技术社区  · 16 年前

    我正在寻找一种方法,将一个基数为10的数字转换成一个基数为N的数字,其中N可以很大。具体来说,我是在看转换到基地-85和回来。有人知道一个简单的算法来执行转换吗?理想情况下,它将提供如下内容:

    to_radix(83992, 85) -> [11, 53, 12]
    

    任何想法都很感激!

    罗亚

    8 回复  |  直到 15 年前
        1
  •  20
  •   Jörg W Mittag    16 年前

    这是个有趣的问题,所以我有点过火了:

    class Integer
      def to_base(base=10)
        return [0] if zero?
        raise ArgumentError, 'base must be greater than zero' unless base > 0
        num = abs
        return [1] * num if base == 1
        [].tap do |digits|
          while num > 0
            digits.unshift num % base
            num /= base
          end
        end
      end
    end
    

    这适用于任意基。它只适用于整数,尽管没有理由不能扩展到任何任意数。而且,它忽略了数字的符号。再说一遍,没有理由这么做 这样做,但主要是我不想在返回值中提出一个返回符号的约定。

    class Integer
      old_to_s = instance_method(:to_s)
      define_method :to_s do |base=10, mapping=nil, sep=''|
        return old_to_s.bind(self).(base) unless mapping || base > 36
        mapping ||= '0123456789abcdefghijklmnopqrstuvwxyz'
        return to_base(base).map {|digit| mapping[digit].to_s }.join(sep)
      end
    end
    
    [Fixnum, Bignum].each do |klass|
      old_to_s = klass.instance_method(:to_s)
      klass.send :define_method, :to_s do |base=10, mapping=nil, sep=''|
        return old_to_s.bind(self).(base) unless mapping || base > 36
        return super(base, mapping, sep) if mapping
        return super(base)
      end
    end
    

    to_s [] 到\u s . 因此,一个字符串是完美的,但是一个整数数组也可以。)

    它还接受一个可选的分隔符,用于分隔数字。

    例如,这允许您通过将IPv4地址视为base-256数字并使用标识进行映射和设置,来格式化IPv4地址 '.' 作为分隔符:

    2_078_934_278.to_s(256, Array.new(256) {|i| i }, '.') # => '123.234.5.6'
    

    require 'test/unit'
    class TestBaseConversion < Test::Unit::TestCase
      def test_that_83992_in_base_85_is_11_53_12
        assert_equal [11, 53, 12], 83992.to_base(85)
      end
      def test_that_83992_in_base_37_is_1_24_13_2
        assert_equal [1, 24, 13, 2], 83992.to_base(37)
      end
      def test_that_84026_in_base_37_is_1_24_13_36
        assert_equal [1, 24, 13, 36], 84026.to_base(37)
      end
      def test_that_0_in_any_base_is_0
        100.times do |base|
          assert_equal [0], 0.to_base(base)
          assert_equal [0], 0.to_base(1 << base)
          assert_equal [0], 0.to_base(base << base)
        end
      end
      def test_that_84026_in_base_37_prints_1od_
        assert_equal '1od_', 84026.to_s(37, '0123456789abcdefghijklmnopqrstuvwxyz_')
      end
      def test_that_ip_address_formatting_works
        addr = 2_078_934_278
        assert_equal '123.234.5.6', addr.to_s(256, (0..255).to_a, '.')
        assert_equal '123.234.5.6', addr.to_s(256, Array.new(256) {|i| i}, '.')
      end
      def test_that_old_to_s_still_works
        assert_equal '84026', 84026.to_s
        assert_equal '1su2', 84026.to_s(36)
      end
    end
    
        2
  •  3
  •   cletus    16 年前

    这方面的伪代码相当简单。从无符号整数以85为基数:

    digits := '';
    while (number > 0)
      digit := number % 85
      digits := base85Digit(digit) + digits
      number /= 85 // integer division so the remainder is rounded off
    end while
    

    以10为基数:

    mult := 1
    result := 0
    for each digit in digits // starting from the rightmost working left
      result += base10(digit) * mult
      mult *= 85
    end for
    
        3
  •  1
  •   Amber    16 年前

    只是一个通用的伪码算法:

    1. 取当前数字mod base,将结果存储在列表前面
        4
  •  0
  •   Yin Zhu    16 年前
    83992 / 85 = 988, reminder 12
    
    988   / 85 = 11,  reminder 53
    
    11   /  85 = 0,   reminder 11
    

    以相反的顺序写下提醒:11,53,12得到你的基数85。

    要取回它:

    11 * 85^2 + 53 * 85^1 + 12 * 85^0 = 83992
    
        5
  •  0
  •   Andrew Grimm Alex Wayne    16 年前

    Fixnum#to_s 不会帮你的,因为它只会上升到 base 36 .

        6
  •  0
  •   Mark M    16 年前

    我能想到的最简单的算法是(在伪代码中):

    N = base-10 number
    1) N mod 85 = 1st number
    2) tempVal = floor(N/85)
    3) if(tempVal > 0 && tempVal < 85) then
        tempVal= 2nd number
    else
        2nd number = (tempVal mod 85), then goto step (2), replacing N with N1
    
        7
  •  0
  •   Rex Kerr    16 年前

    base85对于二进制数据的ASCII编码特别有用,我想这就是您使用它的目的(然而,如果这就是为什么你应该问问自己,它是否真的值得额外的麻烦,以及64进制是否不够好。)

    如果您将此用作编码方案,您的工作将是将整数(4字节)转换为5个base85数字的组(如何处理不是4字节倍数的事情取决于你——通常结尾用零填充。有关详细信息,请参见Base85上的Wikipedia页面。)

    // To base 85
    unsigned int n = // your number
    byte b85[5]; // What you want to fill
    for (int i=0 ; i<5 ; i++) {
      b85[4-i] = (n%85);  // Fill backwards to get most significant value at front
      n = n/85;
    }
    
    // From base 85
    n = 0;
    for (int i=0 ; i< 5 ; i++) {
      n = n*85 + b85[i];
    }
    

    这不必担心溢出,不必担心添加33以进入ASCII范围,也不必担心将0编码为 z !!!!! ,等等。

        8
  •  0
  •   Emirikol    11 年前

    def to_radix(int, radix)
      int == 0 ? [] : (to_radix(int / radix, radix) + [int % radix])
    end