Algorithm 如何用正方形完全填充固定的矩形?

Algorithm 如何用正方形完全填充固定的矩形?,algorithm,math,knapsack-problem,packing,bin-packing,Algorithm,Math,Knapsack Problem,Packing,Bin Packing,这是背包算法还是装箱?我找不到一个精确的解决方案,但基本上我有一个固定的矩形区域,我想用完美的正方形填充,代表我的项目,每个项目都有不同的权重,这将影响它们相对于其他项目的大小 它们将从左上角到右下角从大到小排序 此外,尽管我需要完美的正方形,但最终允许一些非均匀缩放填充整个空间,只要它们仍保留其相对面积,并且非均匀缩放以尽可能少的量完成 我可以用什么算法来实现这一点呢?有一个问题是由于长垣广史和安倍晋介。我在C++中实现它,注意用最坏情况递归深度O(log n)获得最坏情况O(n log n)

这是背包算法还是装箱?我找不到一个精确的解决方案,但基本上我有一个固定的矩形区域,我想用完美的正方形填充,代表我的项目,每个项目都有不同的权重,这将影响它们相对于其他项目的大小

它们将从左上角到右下角从大到小排序

此外,尽管我需要完美的正方形,但最终允许一些非均匀缩放填充整个空间,只要它们仍保留其相对面积,并且非均匀缩放以尽可能少的量完成

我可以用什么算法来实现这一点呢?

有一个问题是由于长垣广史和安倍晋介。我在C++中实现它,注意用最坏情况递归深度O(log n)获得最坏情况O(n log n)-时间实现。如果n≤ 100,这些预防措施可能是不必要的

#include <algorithm>
#include <iostream>
#include <random>
#include <vector>

namespace {

struct Rectangle {
  double x;
  double y;
  double width;
  double height;
};

Rectangle Slice(Rectangle &r, const double beta) {
  const double alpha = 1 - beta;
  if (r.width > r.height) {
    const double alpha_width = alpha * r.width;
    const double beta_width = beta * r.width;
    r.width = alpha_width;
    return {r.x + alpha_width, r.y, beta_width, r.height};
  }
  const double alpha_height = alpha * r.height;
  const double beta_height = beta * r.height;
  r.height = alpha_height;
  return {r.x, r.y + alpha_height, r.width, beta_height};
}

void LayoutRecursive(const std::vector<double> &reals, const std::size_t begin,
                     std::size_t end, double sum, Rectangle rectangle,
                     std::vector<Rectangle> &dissection) {
  while (end - begin > 1) {
    double suffix_sum = reals[end - 1];
    std::size_t mid = end - 1;
    while (mid > begin + 1 && suffix_sum + reals[mid - 1] <= 2 * sum / 3) {
      suffix_sum += reals[mid - 1];
      mid -= 1;
    }
    LayoutRecursive(reals, mid, end, suffix_sum,
                    Slice(rectangle, suffix_sum / sum), dissection);
    end = mid;
    sum -= suffix_sum;
  }
  dissection.push_back(rectangle);
}

std::vector<Rectangle> Layout(std::vector<double> reals,
                              const Rectangle rectangle) {
  std::sort(reals.begin(), reals.end());
  std::vector<Rectangle> dissection;
  dissection.reserve(reals.size());
  LayoutRecursive(reals, 0, reals.size(),
                  std::accumulate(reals.begin(), reals.end(), double{0}),
                  rectangle, dissection);
  return dissection;
}

std::vector<double> RandomReals(const std::size_t n) {
  std::vector<double> reals(n);
  std::exponential_distribution<> dist;
  std::default_random_engine gen;
  for (double &real : reals) {
    real = dist(gen);
  }
  return reals;
}

} // namespace

int main() {
  const std::vector<Rectangle> dissection =
      Layout(RandomReals(100), {72, 72, 6.5 * 72, 9 * 72});
  std::cout << "%!PS\n";
  for (const Rectangle &r : dissection) {
    std::cout << r.x << " " << r.y << " " << r.width << " " << r.height
              << " rectstroke\n";
  }
  std::cout << "showpage\n";
}

#include

好,让我们假设整数位置和大小(无浮点操作)。要将矩形均匀划分为规则的正方形网格(尽可能大的正方形),单元格的大小将是矩形大小的最大公约数GCD

但是你想要的方块比这个少很多,所以我会尝试这样的方法:

  • 尝试所有方形尺寸
    a
    从1到更小的矩形尺寸

  • 对于每个
    a
    计算剩余矩形的原始正方形网格大小,只要
    a*a
    正方形从中切下

    因此,只要将
    a*a
    square切掉,就可以在2个矩形上创建GCD。如果2个矩形中的所有3个大小<代码> > <代码>和GCD大于1(忽略零区域矩形),则考虑<代码> < < /代码>作为有效的解决方案,所以记住它。
  • for
    循环之后,使用上次找到的有效
    a

    因此,只需将
    a*a
    square添加到您的输出中,然后对
    a*a
    square被切断后保留在原始矩形上的两个矩形再次递归地执行此操作

  • 这里是简单的C++/VCL/OpenGL示例:

    //---------------------------------------------------------------------------
    #包括
    #布拉格语hdrstop
    #包括“Unit1.h”
    #包括“gl_simple.h”
    //---------------------------------------------------------------------------
    #pragma包(智能初始化)
    #pragma资源“*.dfm”
    TForm1*Form1;
    //---------------------------------------------------------------------------
    类方//简单方
    {
    公众:
    int x,y,a;//角2D位置和大小
    square(){x=y=a=0.0;}
    平方(int x,int y,int a){x=x;y=y;a=a;}
    ~square(){}
    作废提款()
    {
    glBegin(GL_线_环);
    glVertex2i(x,y);
    glVertex2i(x+a,y);
    glVertex2i(x+a,y+a);
    glVertex2i(x,y+a);
    格伦德();
    }
    };
    int rec[4]={20,20760560};//x、 y,a,b
    常数int N=1024;//最大平方数
    int n=0;//方块数
    正方形s[N];//方格
    //---------------------------------------------------------------------------
    intgcd(inta,intb)//慢欧几里德gcd
    {
    如果(!b)返回a;
    返回gcd(b,a%b);
    }
    //---------------------------------------------------------------------------
    无效计算(整数x0、整数y0、整数xs、整数ys)
    {
    if((xs==0)| |(ys==0))返回;
    常数int x1=x0+xs;
    常数int y1=y0+ys;
    int a,b,i,x,y;
    平方t;
    //试着先找到最大的正方形
    对于(a=1,b=0;(ax))i=x;
    如果((y>0)&(i>y))i=y;
    //如果整除比1更好,记住它是更好的解
    如果(i>1)b=a;
    }a=b;
    //找不到更大的正方形,所以使用朴素的正方形网格划分
    
    如果(aI不认为完美的正方形在二进制计算机上总是可能的…更像是一些浮点精度限制,如矩形大小的2^-24。我会计算最大公因数(但浮点1)在第一次迭代中,我们可以将矩形大小浮动对齐到同一个指数,并将尾数用作GCD的整数。换句话说,放大矩形,使其大小为精度限制内的整数如果你想的话,你可以把它们合并成更大的正方形……看这一点,在这里,你需要计算任何浮动大小矩形的GCD。这似乎很难做到,特别是在排序标准下。我知道,它允许对正方形大小或纵横比进行很少的控制。这很容易,但也许你有理由这样做想要正方形。谢谢伙计们,什么是GCD?我刚刚发现它叫树形映射。我想我可以用它。我只是觉得完美的正方形看起来更好。而且在我的情况下,我没有那么多的项目,比如说最多100个。如果你愿意放弃排序,可能可以优化一棵树,使树形映射接近1:1pect比率看起来不像squares@Spektre“允许某些非均匀缩放”引用OP:
    非均匀缩放是以尽可能少的量完成的
    我在你的输出中没有看到正方形…@Spektre这似乎是一个难题。请随意发布你自己的答案。我会的,但有很多关于输入缺失的澄清,如果没有它,回答并实现OP作者想要的不同是浪费时间的输入的组合……看起来他也缺少这方面所需的数学技能(假设GCD是什么)尽管有很高的代表性…但它可能只是太不同的语言…有时我常常不懂英语中的一些数学,因为它使用不同的符号。很多情况下,我会尝试将其移植到C。但是