【问题标题】:How to create n strings of random lengths the sum of which is equal to a given amount?如何创建n个随机长度的字符串,其总和等于给定数量?
【发布时间】:2017-06-05 17:03:33
【问题描述】:

我正在尝试创建一种算法来创建随机长度的 n 字符串,其总和等于给定数量。

一个更清楚的例子:

total = 20;
n = 7;

strings = ['aaaa', 'a', 'aaaaaaa', 'aa', 'aaa', 'aa', 'a'];

所以我有 7 个随机长度的字符串,它们各自长度的总和是(除非我进行了错误计数)20。

直到现在我想出了这个递归函数:

gaps = [];
function createGapsArray(total, n) {
    if (n == 1) {
        var gapLength = total;
    } else {
        var gapLength = getRandomInt(1, total / 2);
    }

  var gap = "";
  for (var i = 0; i < gapLength; i++) {
    gap += "a";
  }
  gaps.push(gap);

  if (n > 1 && total > 0) {
    createGapsArray(total - gapLength, --n);
  }
}

这实际上不起作用。通常它在生成我想要的所有 n 段之前完成。通过我完成的少数测试,似乎将总数除以 4 而不是 2 可以完成工作。喜欢:

var gapLength = getRandomInt(1, total / 4);

但是这个约束的选择是任意的。我想知道是否有更好的方法。

另外,我知道,通过我的方法,算法最初可能会生成较长的片段,而最终会生成较小的片段。我不介意均匀分布,但这没什么大不了的,因为我可以在完成后简单地对数组进行洗牌。

【问题讨论】:

    标签: arrays algorithm random


    【解决方案1】:

    这是一个类似的问题“找到一个随机的 k 数字集合,其和为 N”。该答案的原始版本使用了一个简单的解决方案,如果数字是连续的(即浮点数),则该解决方案是无偏的:在[0, N] 范围内生成 k-1 个数字,对它们进行排序,将 0 放在开头,将 N 放在末尾,然后找出连续元素之间的差异。但是由于没有小数字符,我们需要数字是整数,并且上述算法偏向于包含 0 的集合(在连续情况下得到 0 的概率很小,但在离散情况下很重要)。

    生成非空整数解的无偏解是在包含范围 [1, N-1] 内找到整数的随机 (k-1)-组合。要找到随机组合,请使用该范围的随机 shuffle 的前 k-1 个元素(使用 Fisher-Yates shuffle)。然后对组合进行排序(如果需要)并在前面加上 0;这些值是每个字符串的起始位置(因此下一个值是结束位置。)

    这不会产生空子字符串,因为每个子字符串都有一个唯一的起点。要包含空子字符串,请使用上面的 N+k 而不是 N,然后将每个范围缩小 1:如果索引已排序,您可以通过从 i 中减去 i 来实现索引。

    在 Python 中:

    from random import sample
    def random_split(str, k):
        v = [0] + sorted(sample(range(1, len(str)), k-1)) + [len(str)]
        return [str[v[i]:v[i+1]] for i in range(k)]
    
    def random_split_allow_empty(str, k):
        v = [0] + sorted(sample(range(1, len(str)+k), k-1)) + [len(str)+k]
            return [str[v[i]-i:v[i+1]-i-1] for i in range(k)]
    

    在 Javascript 中:

    function shuffle(vec, k) {
      for (let i = 0; i < k; ++i) {
        let r = i + Math.floor(Math.random() * (vec.length - i));
        let t = vec[r];
        vec[r] = vec[i];
        vec[i] = t;
      }
      return vec;
    }
    
    function random_partition(N, k) {
      let v = [];
      for (let i = 1; i < N; ++i) v[i-1] = i;
      shuffle(v, k - 1);
      v[k-1] = 0;
      return v.slice(0, k).sort((a,b)=>a-b);
    }
    
    function random_split(s, k) {
      return random_partition(s.length, k).map(
        (v, i, a) => s.slice(v, a[i+1]));
    }
    
    function random_split_allow_empty(s, k) {
      return random_partition(s.length + k, k).map((v,i)=>v-i).map(
        (v, i, a) => s.slice(v, a[i+1]));
    }
    

    【讨论】:

    • 感谢您的回答!我会花时间尝试一下。也感谢你用 javascript 编写它
    • 这绝对是最完整的解决方案,所以我会接受这个答案
    【解决方案2】:

    严格来说,您不能这样做,因为您至少要向违反所需随机性属性的最后一个“字符串”添加约束。当然,您需要多严格地解释随机性要求很大程度上取决于您在更大的问题域中所做的工作。

    您可以做的是从所需长度的初始字符串开始,然后迭代生成随机分割点,直到获得所需数量的片段。要真正随机,您必须允许其中一些片段为空(例如,如果您随机选择一个单字符片段进行拆分会发生什么?),但这可能可以在不做太多暴力的情况下解决整体结果的随机性……

    【讨论】:

    • 对于非空字符串,您可以从长度为total-n 的字符串开始,执行与空字符串相同的操作,但只需在每个字符串末尾添加一个字符。
    • 我对真正的随机性并不感兴趣。您的方法很有趣,但是您将如何进行拆分?如果在第一次拆分时我得到一个长 n-1 的字符串怎么办?我应该一直继续分割最长的片段吗?
    • 首先将初始字符串放入列表中。在每次迭代中,从列表中随机选择一个字符串(称为x),生成一个介于 0 和 len(x) - 1 之间的随机数(可选地介于 1 和 len(x) - 2 之间以避免创建空字符串),在该位置拆分字符串, 并将其替换为两件。迭代直到获得目标数量的片段。如果需要,添加适当的测试以避免拆分单个字符串。
    【解决方案3】:

    将其视为长度等于总长度的字符串。我以20为例:

    string: aaaaaaaaaaaaaaaaaaaa
    index:  01234567890123456789
    

    然后您在0total 之间生成N-1 个随机数,这些随机数对应于您应该剪切字符串以生成N 个不同字符串的位置。

    假设数字是 5、8、13、15、1、3。这些将是切割字符串的索引:

    string: a aa aa aaa aaaaa aa aaaaa
    index:  0|12|34|567|89012|34|56789
    

    这和生成N-1个随机数,排序,在列表的开头添加0,在末尾添加total,取差值是一样的:

    var numbers = [0];
    for (var i = 0; i < N-1; i++) numbers.push(getRandomInt(0, total));
    numbers.push(total);
    numbers.sort();
    
    var strings = [];
    for (var i = 0; i < N; i++)
      strings.push(new Array(numbers[i + 1] - numbers[i]).join('a'));
    

    这将提供均匀分布。

    请注意,如果您只想要非空字符串,则不应有重复的随机值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 2018-01-25
      • 1970-01-01
      相关资源
      最近更新 更多