Найти центр N клеток, которые вместе строить прямоугольник


У меня есть прямоугольник с размером Вт и высотой Н. Теперь я хочу разделить этот прямоугольник В Н новые прямоугольники, которые являются как можно более близкими к квадрату. После этого я хотел бы вычислили центре каждого квадрата.

public static List<Point> getCenters(int number, double width, double height)
    {
        List<Point> points = new List<Point>();
        int originalNumberOfSquares = number;
        int numberOfSquares = number;
        if (numberOfSquares % 2 == 1)
        {
            numberOfSquares++;
        }
        int rectangleWidth = Convert.ToInt32(width);
        int rectangleHeight = Convert.ToInt32(height);

        double minDistance = Double.MaxValue;
        int nSquaresInRow = -1;
        int nSquaresInColumn = -1;

        for (int i = 0; i <= numberOfSquares; i++)
        {
            for (int j = 0; j <= numberOfSquares; j++)
            {
                if (i * j == numberOfSquares)
                {
                    if (Math.Abs(i - j) < minDistance)
                    {
                        minDistance = Math.Abs(i - j);
                        nSquaresInRow = i;
                        nSquaresInColumn = j;
                    }
                }
            }
        }

        int squareWidth = rectangleWidth / nSquaresInColumn;
        int squareHeight = rectangleHeight / nSquaresInRow;

        for (int r = 0; r < originalNumberOfSquares; r++)
        {
            int xSquareCenter = (((r + 1) * 2) - 1) * (squareWidth / 2);
            while (xSquareCenter > rectangleWidth)
            {
                xSquareCenter = xSquareCenter - rectangleWidth;
            }

            int row = (r / nSquaresInColumn) + 1;
            int ySquareCenter = ((2 * row) - 1) * (squareHeight / 2);

            points.Add(new Point(Convert.ToDouble(xSquareCenter), Convert.ToDouble(ySquareCenter)));
        }

        return points;
    }

Теперь код работает, но я думаю, что это немного некрасиво. Любой подсказки о том, как я могу улучшить его?



Комментарии
3 ответа

Пару заметок:


  • Ширина, высота и количество вложенных прямоугольников должны быть параметры, а не локальные переменные. Если вы, например, хотите распечатать центров для различных значений ш, ч и п, вызов метода в цикле с разными аргументами является более удобным, чем менять код, а запустить его несколько раз.

  • Определение размера вложенных прямоугольников и сбора их центров может осуществляться в два отдельных метода, потому что а) они легко могут быть разделены, пока они не переплелись б) он делает это сразу видно, в какой части осуществляется, какой код и C) они могут быть полезными сами по себе.

  • Вместо вывода центры, вы должны вернуть их в массив или список. Таким образом вы можете легко использовать этот метод в условиях, когда вы не хотите печатать центров или хотите сделать что-то в них перед печатью. Кроме того, это обычно хорошая практика, чтобы отделить код ИО от логики кода.

  • Так как и я и J не являются ИНЦ и, таким образом, minDistance только когда-нибудь занимать целые значения, то он должен иметь тип int и, не двойной. В противном случае он оставляет впечатление, что он может иметь нецелое значение.

  • На подобной ноте, вы должны, вероятно, сделать типа ширина и высота инт , так как первое, что вам сделать, это обрезать их инты. Имея свой тип можно дважды может сделать пользователь ваш способ думаю, что результаты будут более точными, чем они есть на самом деле.

5
ответ дан 9 февраля 2011 в 02:02 Источник Поделиться

Ваш первый шаг должен быть, чтобы использовать осмысленные имена переменных, даже для вашего цикла переменных. Я никогда не смотрел на этот код раньше. Я понятия не имею, что он делает. Когда другие члены вашей команды (если он есть), посмотрите на этот код, они просто не понимает, что происходит. Если вы вернетесь к этому коду, через несколько дней, недель, месяцев и т. д. вы будете в одной лодке, как и остальные из нас. Как только вы дали хорошие имена переменных, так будет проще рассуждать о коде для себя и любого, кто придет после вас. В свою очередь, будет легче дать дополнительные рекомендации рефакторинг, если это уместно.

3
ответ дан 9 февраля 2011 в 02:02 Источник Поделиться

1) Ваш код не ручка количество=0 правильно в любом случае (он будет бросать на Инт squareWidth = rectangleWidth / nSquaresInColumn;) так я хотел начать свой первый петлями (я,J в итераторы) с 1 с 0 строк/столбцов не подходит в любом случае.

2) Вы фактически не нуждаетесь в J цикле. Вместо:

    for (int i = 0; i <= numberOfSquares; i++)
{
for (int j = 0; j <= numberOfSquares; j++)
{
if (i * j == numberOfSquares) { ... }
}
}

Его можно упростить до (и учитывая мой #1):

    for (int i = 1; i <= numberOfSquares; i++)
{
int j = numberOfSquares / i;
if (i * j == numberOfSquares) { ... }
}

3) Вам не нужно перебирать до numberOfSquares здесь: для (тип int я = 1; я <= numberOfSquares; я++). Поскольку я=2,то J=6 такой же как я=6 и J=2 и ты смотришь только на первой можно перебирать до корня квадратного из numberOfSquares:

int maxI = (int)Math.Sqrt(numberOfSquares);
for (int i = 1; i <= maxI; i++)

4) Поскольку вы ищете минимальный Я-J разницы вы должны начать повторять от ближайшего и остановки итераций, как только вы найдете подходящую пару. Оригинал:

    int maxI = (int)Math.Sqrt(numberOfSquares);
for (int i = 1; i <= maxI; i++)
{
int j = numberOfSquares / i;
if (i * j == numberOfSquares)
{
if (Math.Abs(i - j) < minDistance)
{
minDistance = Math.Abs(i - j);
nSquaresInRow = i;
nSquaresInColumn = j;
}
}
}

Изменен:

    int maxI = (int)Math.Sqrt(numberOfSquares);
for (int i = maxI; i >= 1; i--) // looping in reverse order
{
int j = numberOfSquares / i;
if (i * j == numberOfSquares)
{
// if (Math.Abs(i - j) < minDistance) we do not need this check anymore
{
minDistance = Math.Abs(i - j);
nSquaresInRow = i;
nSquaresInColumn = j;
break;
}
}
}

5) с моим #3 смены вам не понадобится математика.АБС больше, поскольку я всегда меньше или равен, чем Джей:

minDistance = j - i;

Теперь перейдем к второй петли:

6) инт xSquareCenter = (((Р + 1) * 2) - 1) * (squareWidth / 2); здесь двойная математике должны быть использованы, в противном случае ты теряешь точность здесь.

7) Также эта линия является немного трудно понять. Я бы ввел строку и столбец переменных, чтобы сделать его более ясным:

int column = r % nSquaresInRow;
double xSquareCenter = (column + 0.5) * squareWidth;
int row = r / nSquaresInRow;
double ySquareCenter = (row + 0.5) * squareHeight;

8) последняя вещь, я бы заменил обе петли с LINQ. Конечный результат:

public static List<Point> getCenters(int number, double width, double height)
{
List<Point> points = new List<Point>();
int originalNumberOfSquares = number;
int numberOfSquares = number;
if (numberOfSquares % 2 == 1)
{
numberOfSquares++;
}
int rectangleWidth = Convert.ToInt32(width);
int rectangleHeight = Convert.ToInt32(height);

int nSquaresInRow = -1;
int nSquaresInColumn = -1;

int maxI = (int)Math.Sqrt(numberOfSquares);
int nSquaresInRow
= Enumerable.Range(1, maxI)
.Reverse()
.First(i => numberOfSquares % i == 0);
int nSquaresInColumn = numberOfSquares / nSquaresInRow;

int squareWidth = rectangleWidth / nSquaresInColumn;
int squareHeight = rectangleHeight / nSquaresInRow;

return Enumerable.Range(0, originalNumberOfSquares)
.Select(r => new { Column = r % nSquaresInRow, Row = r / nSquaresInRow})
.Select(p => new {
xSquareCenter = (p.Column + 0.5) * squareWidth,
ySquareCenter = (p.Row + 0.5) * squareHeight})
.Select(p => new Point(p.xSquareCenter, p.ySquareCenter))
.ToList();
}

2
ответ дан 6 марта 2011 в 11:03 Источник Поделиться