【问题标题】:Randomly Generate Letters According to their Frequency of Use?根据使用频率随机生成字母?
【发布时间】:2011-01-10 03:09:29
【问题描述】:

如何根据常用语音中的使用频率随机生成字母?

任何伪代码都值得赞赏,但 Java 中的实现会很棒。否则,只需朝正确的方向戳一下就会有帮助。

注意:我不需要生成使用频率 - 我确信我可以轻松查找。

【问题讨论】:

  • 欺骗stackoverflow.com/questions/2073235/random-weighted-choice 和许多其他人(搜索“加权随机生成”)
  • @Eli:抱歉 - 没有意识到它的名字。
  • fEnglish = new[] {8.167f,1.492f,2.782f,4.253f,12.702f,2.228f,2.015f,6.094f, 6.966f,0.153f,0.772f,4.025f,2.406f,6.749f,7.507f,1.929f,0.095f,5.987f, 6.327f,9.056f,2.758f,0.978f,2.361f,0.150f,1.974f,0.074f}; 然后...
  • public static int RandomFromFrequencyArray(this float[] f) { float sum = 0f; foreach (float ff in f) sum += ff; int kF = f.Length; int result = 0; float sumSoFar = f[0]; float percentageResult = Random.Range(0f, sum ); while (sumSoFar < percentageResult) { ++result; sumSoFar += f[result]; if ( result >= kF ) {Debug.Log("woe..."); return (kF-1);} } return result; }
  • 频率数组不必加到 100。所以,这样做完全没问题:'(new[] {15f,5f,5f,1f}).RandomFromFrequencyArray();` For例如英语中的元音...只需从整个字母频率中取频率(因为它不必加到 100)...'int trueRandomVowel = (new[] {8.167f,12.702f,6.966f,7.507 f,2.758f}).RandomFromFrequencyArray(); return ("aeiou".ToCharArray())[v].ToString();'

标签: java algorithm random pseudocode


【解决方案1】:

我假设您将频率存储为 0 到 1 之间的浮点数,总和为 1。

首先你应该准备一个累积频率表,即那个字母和它之前的所有字母的频率之和。

为了简化,如果你从这个频率分布开始:

A  0.1
B  0.3
C  0.4
D  0.2

您的累积频率表将是:

A  0.1
B  0.4 (= 0.1 + 0.3)
C  0.8 (= 0.1 + 0.3 + 0.4)
D  1.0 (= 0.1 + 0.3 + 0.4 + 0.2)

现在生成一个介于 0 和 1 之间的随机数,并查看该数字在此列表中的位置。选择最小累积频率大于随机数的字母。一些例子:

假设你随机选择 0.612。这介于 0.4 和 0.8 之间,即 B 和 C 之间,所以你会选择 C。

如果你的随机数是 0.039,它在 0.1 之前,即在 A 之前,所以选择 A。

我希望这是有道理的,否则请随时要求澄清!

【讨论】:

    【解决方案2】:

    一种快速的方法是生成一个字母列表,其中每个字母根据其出现的频率出现在列表中。比如说,如果“e”的使用率为 25.6%,并且您的列表长度为 1000,那么它将有 256 个“e”。

    然后,您可以通过使用(int) (Math.random() * 1000) 从列表中随机选择点来生成 0 到 999 之间的随机数。

    【讨论】:

    • 匹配特定文本的字母频率的最佳方法:-)
    • +1 这是一个很好的建议,但如果您的字符出现频率非常低(例如 0.00001 或更少),则不理想。我想这取决于你需要什么。
    • 这有明显的精度限制,但可能更可取,因为它很容易实现和理解。
    • 这确实是随机性的“视频游戏方法”..您列出了一个列表,其中包含您想要的粗略频率的东西 - 然后选择一个。
    【解决方案3】:

    我要做的是将相对频率缩放为浮点数,使其总和为 1.0。然后,我将创建一个包含每个字母的 cumulative 总数的数组,即获得该字母和所有“低于”它的字母必须加到顶部的数字。假设 A 的频率为 10%,b 为 2%,z 为 1%;那么您的表格将如下所示:

    0.000 A ; from 0% to 10% gets you an A
    0.100 B ; above 10% is at least a B
    0.120 C ; 12% for C...
    ...
    0.990 Z ; if your number is >= 99% then you get a Z
    

    然后您自己生成一个介于 0.0 和 1.0 之间的随机数,并在数组中进行二分搜索,以找到小于您的随机数的第一个数字。然后选择那个位置的字母。完成。

    【讨论】:

    • 诅咒你,马克拜尔斯!更好解释的答案,比我快 3 分钟! ;)
    【解决方案4】:

    甚至不是伪代码,但可能的方法如下:

    令 p1, p2, ..., pk 为您想要匹配的频率。

    1. 计算累积频率:p1, p1+p2, p1+p2+p3, ... , 1
    2. 生成一个随机统一的 (0,1) 数 x
    3. 检查累积频率 x 属于哪个区间:如果它在 p1+..+pi 和 p1+...+pi+p(i+1) 之间,则输出 (i+1)st信

    根据您实现区间查找的方式,如果 p1,p2,... 按降序排序,该过程通常会更有效,因为您通常会更快找到包含 x 的区间。

    【讨论】:

      【解决方案5】:

      使用二叉树为您提供了一种很好、干净的方式来找到正确的条目。在这里,您从frequency 映射开始,其中键是符号(英文字母),值是它们出现的频率。这会被反转,并创建一个NavigableMap,其中键是累积概率,值是符号。这使查找变得容易。

        private final Random generator = new Random();
      
        private final NavigableMap<Float, Integer> table = 
          new TreeMap<Float, Integer>();
      
        private final float max;
      
        public Frequency(Map<Integer, Float> frequency)
        {
          float total = 0;
          for (Map.Entry<Integer, Float> e : frequency.entrySet()) {
            total += e.getValue();
            table.put(total, e.getKey());
          }
          max = total;
        }
      
        /** 
         * Choose a random symbol. The choices are weighted by frequency.
         */ 
        public int roll()
        {
          Float key = generator.nextFloat() * max;
          return table.higherEntry(key).getValue();
        }
      

      【讨论】:

        猜你喜欢
        • 2012-09-03
        • 2010-12-02
        • 2013-02-22
        • 1970-01-01
        • 1970-01-01
        • 2015-02-05
        • 1970-01-01
        • 2022-12-18
        相关资源
        最近更新 更多