确定给定数字所需的行数/列数

14
我有许多控件(这里是图表),可以在运行时确定。我想将它们放在一个栅格中,栅格中的行数和列数应该正确匹配。例如:
  • 4个项目= 2 x 2
  • 8个项目= 4 x 2
  • 9个项目= 3 x 3
  • 20个项目= 5 x 4
  • 11个项目= 4 x 3(我不关心空单元格)
对不起,我确实没有任何代码可以展示我的尝试。我开始尝试判断平方根是否为整数,如果数字能够被2整除等等,并意识到我不确定如何解决这个问题。但是这是我正在思考的:
  • 如果平方根是一个整数,则使用该平方根作为行数和列数(没有问题)
  • 如果不是,则确保该数字是偶数(如果必须添加1-没有问题)
  • 找到产生该数字的最高两个整数。例如,如果我有20个控件,则网格应为5 x 4而不是10 x 2(不知道最好的方法是什么)
如果有人能指点我正确的方向 - 或者如果我完全错了请建议不同的算法 - 我将不胜感激。

2
是否有其他限制条件可以帮助决定更好的适配方式?例如,2x17 = 34,但是您可以通过使用5x7 = 35来获得一个更“方形”的网格,其中缺少一个空格。随着您开始考虑越来越大的质数的倍数,这种情况可能会变得任意恶化。 - Dan Bryant
1
嗯,我没有考虑到那个问题 - 感谢你指出来。我认为我更喜欢35个正方形,其中一个空格缺失。 - Mike Hildner
6个回答

30

想法:如果平方根不是整数,则向下取整,然后将整数除以此数,再向上取整。

int columns = (int)sqrt(number);
int lines = (int)ceil(number / (float)columns);

例如:21 => 列数 = 4,行数 = 6。

更新:额外奖励是,当(sqrt(number))为整数时,此方法也有效。没有任何四舍五入的情况发生,并且值是正确的。


谢谢!我在打字时重命名了变量,这是个坏主意 ;) - jv42

4
“通常”解决此问题的方法是说,将始终有N列(较少时,始终有N行)。然后,问题就变成了将项目数除以N,这就是您拥有的行数(如果有余数,则再加上一行)。
更改网格的大小会导致令人困惑的用户界面。用户不会理解为什么网格的大小一直在变化。他们不会真正想到它,但他们会因看似随机的变化而感到困惑。
如果您仍然想做您所说的事情,我认为您需要更好地定义您的问题。是否有最大数量的项目可以适合网格?您允许的最大列数是多少?例如,如果允许50个项目,它们应该分为2个项目的25行吗?10个项目的5行?5个项目的10行?
在某个时候,您将不得不水平滚动或说:“最大列数为X”。如果您要强制实施最大列数,则最好只说“将始终有X列”。
除非有强烈的理由使用您要求的可变尺寸网格,否则最好只固定列数。这使得代码变得简单易懂,而不是一些复杂的技巧,并且向您的用户呈现更一致的界面。

确实。我可能会采用这种方法。谢谢。 - Mike Hildner

3

@jv42的解决方案经过快速检查表现良好:

public struct Grid
{
    public int x;
    public int y;

    public Grid(int xx, int yy)
    {
        x = xx;
        y = yy;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Grid g0 = GetGrid(1); Debug.Assert(g0.x == 1 && g0.y == 1);
        Grid g1 = GetGrid(4); Debug.Assert(g1.x == 2 && g1.y == 2);
        Grid g2 = GetGrid(8); Debug.Assert(g2.x == 2 && g2.y == 4);
        Grid g3 = GetGrid(9); Debug.Assert(g3.x == 3 && g3.y == 3);
        Grid g4 = GetGrid(20); Debug.Assert(g4.x == 4 && g4.y == 5);
        Grid g5 = GetGrid(30); Debug.Assert(g5.x == 5 && g5.y == 6);
        Grid g6 = GetGrid(99); Debug.Assert(g6.x == 9 && g6.y == 11);
    }

    public static Grid GetGrid(int n)
    {
        int columns = (int)Math.Sqrt(n);
        int lines   = (int)Math.Ceiling(n / (double)columns);

        return new Grid(columns, lines);
    }

3
感谢您的提问和答案!
以下是转换成 JavaScript 的代码:
cols = Math.floor( Math.sqrt(totalTiles) );
rows = Math.ceil( totalTiles / cols );

0
在WPF中,控件UniformGrid会自动计算网格的行数和列数,而无需确定行数和列数。 例如:
   <UniformGrid >
        <Image Source="Images\Aquarium.jpg"  Margin="5"/>
        <Image Source="Images\Ascent.jpg"   Margin="5" />
        <Image Source="Images\Autumn.jpg"  Margin="5"/>
        <Image Source="Images\Crystal.jpg" Margin="5"/>
        <Image Source="Images\DaVinci.jpg"  Margin="5"/>
        <Image Source="Images\Follow.jpg"  Margin="5"/>
        <Image Source="Images\Friend.jpg" Margin="5"/>
        <Image Source="Images\Aquarium.jpg"  Margin="5"/>
    </UniformGrid>

结果=> 在3列和3行中显示图像


0
我遇到了一个问题,但有一些特定的要求;
  • 列数不能超过某个特定数量
  • 如果项目的数量不能完全适应某个特定的列数,我希望尽可能多地避免出现孤立项。

例如:

1 2 3
4 5 6

1 2 3
4 5

1 2 3 4
5 6 7

我想出了这个(PHP)函数,虽然我确定它可以改进:

<?php
function optimalColCount ($numItems, $maxCols = 4) {
    $numCols = $numItems;

    if ($numCols > $maxCols and $maxCols === 2) {
        $numCols = 2;
    }
    else if ($numCols > $maxCols) {
        $numCols = sqrt($numItems);

        if (!is_int($numCols) or $numCols > $maxCols) {
            $numCols = -1;

            for ($i = $maxCols; $i > 2; $i--) {
                if ($numItems % $i === 0) {
                    $numCols = $i;

                    break;
                }
            }

            if ($numCols === -1) {
                $rests = [];

                for ($i = $maxCols; $i > 2; $i--) {
                    $rests[$i] = $numItems % $i;
                }

                $numCols = array_search(max($rests), $rests);
            }
        }
    }

    return $numCols;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接