【问题标题】:Generate letters to represent number using ruby?使用 ruby​​ 生成字母来表示数字?
【发布时间】:2013-01-31 18:07:34
【问题描述】:

我想生成与数字相对应的字母序列,即“A”、“DE”、“GJE”等。前 26 个很简单,所以 3 返回“C”,26 返回“Z”,27 返回“AA”,28 返回“AB”,依此类推。

我不太清楚的是如何做到这一点,以便它可以处理传入的任何数字。所以如果我传入 4123,我应该返回 3 个字母的组合,因为 (26 * 26 * 26) 允许多达 +17,000 种组合。

有什么建议吗?

【问题讨论】:

  • 这基本上是一个基数为 26 的数字(但基数中没有数字)
  • @SergioTulentsev - 这个问题更复杂,事实证明。我认为导致麻烦的关键特征是A 在某些情况下表示“0”(作为“个”数字),但A 在其他情况下表示“1”(作为“十/二十六”数字。有趣的是,微软的某个人必须解决这个问题;)
  • 我错过了一些东西。为什么“A”特别? A=1*1=1,AA=1*26+1*1=27。
  • @glennmcdonald "A" 并不特别。 “A”总是对应于“1”。特殊的是跳过了“0”。想象一下十进制,向上计数,跳过“0”:1, 2, ..., 8, 9, 11, 12, ..., 18, 19, 21, 22, ..., 98, 99, 111, 112, ... 现在,如果你用七进制(以 27 为底)而不是十进制,你会得到序列。
  • 哦,对了。可能更容易在基数 26 中真正做到这一点,A 为 0。在这个问题中不清楚零的东西是想象解决方案的要求还是工件。

标签: ruby math numerical


【解决方案1】:
class Numeric
  Alph = ("a".."z").to_a
  def alph
    s, q = "", self
    (q, r = (q - 1).divmod(26)); s.prepend(Alph[r]) until q.zero?
    s
  end
end

3.alph
# => "c"
26.alph
# => "z"
27.alph
# => "aa"
4123.alph
# => "fbo"

【讨论】:

  • 我相信这是迄今为止唯一正确的解决方案,尽管我不确定发生了什么......
  • 太棒了。我也从中获得了新的见解。计算总组合的方法类似于 !(26 ^ (n-1))。我不是数学家,所以这可能完全是虚构的,但这会给你三个字母的 (26 ^ 3) + (26 ^ 2) + (26 ^ 1) 和 (26 ^ 2) + (26 ^ 1 ) 两个等。
  • 在 Rails 应用程序中使用它的最佳方法是什么?要将类包含在 lib 目录中并需要它?
  • @JeremyRichards 将其放入您的 config/initializers/numeric.rb 文件中
【解决方案2】:

对@sawa Ruby 2.0 的原始答案进行了调整,因为我无法让他按原样工作:

class Numeric
  Alpha26 = ("a".."z").to_a
  def to_s26
    return "" if self < 1
    s, q = "", self
    loop do
      q, r = (q - 1).divmod(26)
      s.prepend(Alpha26[r]) 
      break if q.zero?
    end
    s
  end
end

这里是从字符串到整数的反转:

class String
  Alpha26 = ("a".."z").to_a

  def to_i26
    result = 0
    downcased = downcase
    (1..length).each do |i|
      char = downcased[-i]
      result += 26**(i-1) * (Alpha26.index(char) + 1)
    end
    result
  end

end

用法:

1234567890.to_s26 
# => "cywoqvj"

"cywoqvj".to_i26  
# => 1234567890

1234567890.to_s26.to_i26
# => 1234567890

"".to_i26
# => 0

0.to_s26
# => ""

【讨论】:

  • 这很好。我已经更新了我的答案,所以任何小于 1 的都将返回一个空字符串。
  • 请注意 String#to_i26 方法会修改它在小写时调用的字符串。要更改它,请将 downcase! 更改为 string = downcasechar = self[-i] 更改为 char = string[-i]
【解决方案3】:

字符串确实有一个succ 方法,所以它们可以在一个范围内使用。 “Z”的继任者恰好是“AA”,所以这是可行的:

h = {}
('A'..'ZZZ').each_with_index{|w, i| h[i+1] = w } 
p h[27] #=> "AA"

【讨论】:

  • 使用这种方法,你不能只是在现场随机计算出某个数字的字符串,而是必须遍历所有数字直到目标数字。
  • 出于性能原因,Sawa 的答案要好得多,但如果您将此响应作为一种快速而肮脏的方法,则以下将在一行中完成:x = 27; ('A'..'ZZZ').reduce(0) {|i, l|如果 (i+1) == x,则中断(l); i+1 }
  • 实际上这比@Sawa 快(根据fruity 17 倍),前提是重新使用哈希。这样做是以使用更多内存为代价的。
  • 这个答案是预先生成固定范围的哈希,只是在需要时引用它。我的答案是当场为任意序列生成它。它们无法比较。
【解决方案4】:

我喜欢这个答案来自:https://stackoverflow.com/a/17785576/514483

number.to_s(26).tr("0123456789abcdefghijklmnopq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")

【讨论】:

  • 这个答案不起作用。问题指出 27 应该返回“AA”,而这个方法返回“BB”
  • 对不起 - 这个答案适用于我不关心生成的确切字母的情况,但我同意这是错误的。
  • 我最终使用了这个,因为我需要一个零
【解决方案5】:

使用找到here 的基本转换方法。我还更改了它,因为我们在这个编号系统中缺少“0”。最终案例已得到解决。

def baseAZ(num)
  # temp variable for converting base
  temp = num

  # the base 26 (az) number
  az = ''

  while temp > 0

    # get the remainder and convert to a letter
    num26 = temp % 26
    temp /= 26

    # offset for lack of "0"
    temp -= 1 if num26 == 0

    az = (num26).to_s(26).tr('0-9a-p', 'ZA-Y') + az
  end

  return az
end

irb I/O:

>> baseAZ(1)
=> "A"
>> baseAZ(26^2 + 1)
=> "Y"
>> baseAZ(26*26 + 1)
=> "ZA"
>> baseAZ(26*26*26 + 1)
=> "YZA"
>> baseAZ(26*26*26 + 26*26 + 1)
=> "ZZA"

【讨论】:

  • 有见地地注意到这个数字系统“缺少一个零”,尽管现在事后看来很明显。我发现 OP 正在寻求转换为 a "Bijective Base-26" system,并且通过该关键字搜索似乎没有基于音译的转换算法。我认为一个简单的基于音译的算法是不可能的,因为在非零系统中总会有一个特定的单数符号必须映射到零系统中的两位数符号。
【解决方案6】:
def letter_sequence(n)
    n.to_s(26).each_char.map {|i| ('A'..'Z').to_a[i.to_i(26)]}.join
end

【讨论】:

  • 不错的解决方案。
  • 根据问题,这不太适用。例如,27 应该返回“AA”,而这会返回“BB”
【解决方案7】:

这是一个简短的基于递归的解决方案

class Numeric
  # 'z' is placed in the begining of the array because 26 % 26 is 0 not 26
  Alph = ['z'] + ("a".."y").to_a

  def to_alph

    # if self is 0 or negative return a blank string.
    # this is also used to end the recursive chain of to_alph calls
    # so don't replace this with raising an error or returning anything else

    return '' if self < 1

    # (otherwise) return two concatenated strings:
    # the right side is the letter for self modules 26
    # the left side is comprised of:
    #  1. minus one, because this is not a zero-based numbering system.
    #     therefore, round numbers (26, 52...) should return one digit 'z'
    #     instead of two digits 'aa' or 'ba'.
    #  2. divide by 26 and call to_alph on that.
    #     this repeats recursively for every digit of the final string,
    #     plus once more that will return '' to end the recursion chain.

    return ((self - 1) / 26).to_alph + Alph[self % 26]
  end
end

【讨论】:

    【解决方案8】:

    根据sawa的回答,我想要一种独立工作的方法,尽管是递归的,以达到预期的结果:

    def num_to_col(num)
      raise("invalid value #{num} for num") unless num > 0
      result, remainder = num.divmod(26)
      if remainder == 0
        result -= 1
        remainder = 26
      end
      final_letter = ('a'..'z').to_a[remainder-1]
      result > 0 ? previous_letters = num_to_col(result) : previous_letters = ''
      "#{previous_letters}#{final_letter}".upcase
    end
    

    【讨论】:

      【解决方案9】:

      您可以通过从其序数中减去 96 来获得字母表中字符的数字位置,如下所示:

      "a".ord - 96
      => 1
      
      "z".ord - 96
      => 26
      

      您还可以通过添加 96 来按数字位置获取字母字符,如下所示:

      (1 + 96).chr
      => "a"
      
      (26 + 96).chr
      => "z"
      

      【讨论】:

        【解决方案10】:

        ('a'..'zzz').to_a[n-1] 将覆盖前 18_278。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-05
          • 2012-11-14
          • 1970-01-01
          • 1970-01-01
          • 2020-11-15
          • 1970-01-01
          • 1970-01-01
          • 2020-10-22
          相关资源
          最近更新 更多