号码分布

Number distribution

提问人:Carra 提问时间:11/29/2011 最后编辑:ShahbazCarra 更新时间:11/17/2023 访问量:251

问:

问题:我们有 x 个复选框,我们想均匀地选中 y 个复选框。

示例 1:选中 50 个复选框,共 100 个复选框。

[-]
[x]
[-]
[x]
...

示例 2:选中 33 个复选框,共 100 个复选框。

[-]
[-]
[x]
[-]
[-]
[x]
...

示例 3:选中 66 个复选框,共 100 个复选框:

[-]
[x]
[x]
[-]
[x]
[x]
...

但是我们很难想出一个公式来检查代码中的它们,尤其是当你进入 11/111 或类似的东西时。有人有想法吗?

算法 与语言无关

评论

0赞 Marc B 11/29/2011
这是什么语言/环境?浏览器?javascript?Windows 应用程序?侏儒?KDE?
0赞 Carra 11/29/2011
C#,但我认为这并不重要,伪代码很好。
0赞 baol 11/29/2011
这个问题是尖锐的:“均匀”的定义并不明确。选择 5 时,10 以上可能均匀为 .x ..xx ...xxx ....xxxx .....xxxxx 甚至......xxxxxx。
0赞 Carra 11/29/2011
这就是为什么我添加了 3 个示例:)

答:

4赞 Shahbaz 11/29/2011 #1

我们首先假设可以被 整除。然后我们表示,解决方案很简单。浏览列表,每个元素,标记其中的 1 个。yxp = y/xp

现在,假设 r = y%x 不为零。仍然 p = y/x,其中 / 是整数表示。因此,您需要:

  • 在第一个 p-r 元素中,标记 1 个元素
  • 在最后一个 r 元素中,标记 2 个元素

注意:这取决于如何定义均匀分布。您可能希望将具有x+1 元素的 r 部分分布在具有 x 元素的 p-r 部分之间,这确实又是同一个问题,并且可以通过递归方式解决。

好吧,所以它实际上不正确。我认为这可以:

无论可分割性如何:

  • 如果 ,则将每个元素标记 1 个元素,次数。y > 2*xp = y/xx
  • 如果 ,则全部标记,并执行上一步取消标记复选框(与前一种情况类似,但替换为y < 2*xy-xyxy-x)

注意:这取决于如何定义均匀分布。例如,您可能希望在 和 元素之间进行更改,以便更好地分布它们。pp+1

评论

0赞 ArjunShankar 11/29/2011
如果 y = 100,x = 66,则:r = 100%66 = 34,p = 100/66 = 1。这使得 p-r 为负数。
0赞 Shahbaz 11/29/2011
@submachine,是的,等等,是这样的,但我必须好好想想
0赞 ArjunShankar 11/29/2011
问题是,当我们看到 100/66 并将其变成 3/2 时,我们在心理上“四舍五入”。这是一个武断的决定,因为我们认为它已经足够接近了。我不认为这可以在只有整数数学的计算机上近似。当然,看看你如何适应它会很有趣!
0赞 Shahbaz 11/29/2011
@submachine,请看我的编辑。我想知道如何决定何时换肤以及何时跳过元素以获得更好的分布。不知何故,我感觉这个问题与在 2D(计算机图形学)中画一条线(你知道,Bresenham 的算法)是一样的,但映射在我的脑海中并不那么清晰p-1p
0赞 Daniel Mošmondor 11/29/2011
有趣的是,无论正确性如何,您的答案的吸引力足以让至少 3 人投赞成票。:)
1赞 Daniel Mošmondor 11/29/2011 #2

假设复选框的数量是 C,X 的数量是 N。

您的示例指出,C=111 和 N=11 是最麻烦的情况。

试试这个:除以 C/N,称之为 D。数组中的索引为双数 I。将另一个变量作为计数器 M。

double D = (double)C / (double)N;
double I = 0.0;
int M = N;
while (M > 0) {
    if (checkboxes[Round(I)].Checked) { //  if we selected it, skip to next
        I += 1.0;
        continue;
    }
    checkboxes[Round(I)].Checked = true;
    M --;
    I += D;
    if (Round(I) >= C) { //  wrap around the end
        I -= C;
    }
}

请注意,Round(x) 应返回 x 的最接近的整数值。

这个可能对你有用。

1赞 Toomai 11/29/2011 #3

我认为关键是要计算您期望每张支票有多少个框。

假设您想要 100 个框中的 33 张支票。,因此您希望每 3.030303 次检查一次...盒。这意味着每 3.030303...框中,您需要添加一个勾选。100 个框中的 66 张支票意味着每 1.51515 张支票一张......框,11 个框中的 111 个检查意味着每 10.090909 个检查一次......盒子,等等。100 / 33 = 3.030303...

double count = 0;
for (int i = 0; i < boxes; i++) {
    count += 1;
    if (count >= boxes/checks) {
        checkboxes[i] = true;
        count -= count.truncate(); // so 1.6 becomes 0.6 - resetting the count but keeping the decimal part to keep track of "partial boxes" so far
    }
}

您可能更愿意使用 as 而不是 for ,或者由于舍入错误,最后一个框可能会被跳过。decimaldoublecount

2赞 Joey Adams 11/29/2011 #4

这是一个使用整数算术的简单解决方案:

void check(char boxes[], int total_count, int check_count)
{
    int i;

    for (i = 0; i < total_count; i++)
        boxes[i] = '-';

    for (i = 0; i < check_count; i++)
        boxes[i * total_count / check_count] = 'x';
}

total_count是框的总数,也是要检查的框数。check_count

首先,它将每个框设置为未选中。然后,它选中复选框,将计数器缩放到框数。check_count

注意:这是左偏的,而不是像你的例子中那样偏右的。也就是说,它打印而不是 .你可以通过替换x--x----x--x

        boxes[i * total_count / check_count] = 'x';

跟:

        boxes[total_count - (i * total_count / check_count) - 1] = 'x';

正确性

假设 ,并且至少可以容纳项目,我们可以证明:0 <= check_count <= total_countboxestotal_count

  • 不会重叠任何复选标记。 每次迭代至少递增 1,因为 .i * total_count / check_counttotal_count >= check_count

  • 这不会溢出缓冲区。下标i * total_count / check_count

    • 将是.、 和 都将是 。>= 0itotal_countcheck_count>= 0

    • 将是.何时和 :< total_countn > 0d > 0

      (n * d - 1) / d < n
      

      换句话说,如果我们取 ,并将分子向下推,商也会下降。n * d / d

      因此,在上述假设下,将小于 。除以零不会发生,因为如果为 0,则相关循环的迭代次数将为零。(check_count - 1) * total_count / check_counttotal_countcheck_count

0赞 James 11/29/2011 #5

快速 html/javascript 解决方案:

<html>
<body>
<div id='container'></div>
<script>
var cbCount = 111;
var cbCheckCount = 11;
var cbRatio = cbCount / cbCheckCount;
var buildCheckCount = 0;

var c = document.getElementById('container');

for (var i=1; i <= cbCount; i++) {
  // make a checkbox
  var cb = document.createElement('input');
  cb.type = 'checkbox';

  test = i / cbRatio - buildCheckCount;
  if (test >= 1) {
    // check the checkbox we just made
    cb.checked = 'checked';
    buildCheckCount++;
  }

  c.appendChild(cb);
  c.appendChild(document.createElement('br'));
}
</script>
</body></html>
0赞 James Waldby - jwpat7 11/29/2011 #6

根据一个问题的答案或本月早些时候的另一个答案改编代码。设置 N = x = 复选框的数量和 M = y = 要检查的数字,并应用截面大小的公式。(另请参阅乔伊·亚当斯(Joey Adams)的回答。(N*i+N)/M - (N*i)/M

在 python 中,改编后的代码是:

  N=100; M=33; p=0;
  for i in range(M):
     k = (N+N*i)/M
     for j in range(p,k-1): print "-",
     print "x",
     p=k

它产生

where [...] 代表 25 次重复。 代码给出

了 where [...] 主要代表重复,中间有一个。
- - x - - x - - x - - x - - [...] x - - x - - - x--xM=66x - x x - x x - x x - x x - [...] x x - x x - x - xxx-x-

请注意,在 C 或 java 中:
用 代替
代替 。
for (i=0; i<M; ++i)for i in range(M):for (j=p; j<k-1; ++j)for j in range(p,k-1):

正确性:请注意,复选框被选中,因为打印“x”是执行时间。M = xM

1赞 MBo 11/29/2011 #7

类似 Bresenham 的算法适用于均匀分布复选框。“x”的输出对应于 Y 坐标的变化。可以选择初始错误作为范围 [0..places) 中的随机值,以避免偏差。

def Distribute(places, stars):
    err = places // 2
    res = ''
    for i in range(0, places):
        err = err - stars
        if err < 0 :
            res = res + 'x'
            err = err + places
        else:
            res = res + '-'
    print(res)

Distribute(24,17)
Distribute(24,12)
Distribute(24,5)

output:

x-xxx-xx-xx-xxx-xx-xxx-x

-x-x-x-x-x-x-x-x-x-x-x-x

--x----x----x---x----x--
0赞 Luka Rahne 11/29/2011 #8

使用 Fisher-Yates shuffle 怎么样?

创建数组,随机播放并选择前 n 个元素。你不需要把它们全部洗牌,只需要数组的前n个。在大多数语言库中都可以找到随机排序。

评论

0赞 Carra 11/29/2011
我考虑过使用随机算法,但没有使用它。对于客户来说,每次打开屏幕时都会看到不同的复选框被选中是很奇怪的,算法每次都必须给出相同的结果。