如何按给定因子计算纵横比?

时间:2011-09-16 08:47:21

标签: java math aspect-ratio

如何按给定因子计算宽高比(格式为integer:integer)?

例如,宽高比16:9的系数为1.778,因为16/9 = 1.778。但是如何通过该因子找到比率?所以

Dimension getAspectRatio(double factor) {
    ...
}

public static void main(String[] arguments) {
    Dimension d = getAspectRatio(16d / 9d);
    System.out.println(d.width + ":" + d.height);
}

应该返回

16:9

8 个答案:

答案 0 :(得分:9)

免责声明:这些算法愚蠢且低效。我相信还有更好的......

找到近似值的一种愚蠢,直截了当(不是非常有效)的算法是这样的:

double ratio = 1.778;
double bestDelta = Double.MAX_VALUE;
int bestI = 0;
int bestJ = 0;

for (int i = 1; i < 100; i++) {
  for (int j = 1; j < 100; j++) {
    double newDelta = Math.abs((double) i / (double) j - ratio);
    if (newDelta < bestDelta) {
      bestDelta = newDelta;
      bestI = i;
      bestJ = j;
    }
  }
}

System.out.println("Closest ratio: " + bestI + "/" + bestJ);
System.out.println("Ratio        : " + ((double) bestI / (double) bestJ));
System.out.println("Inaccurate by: " + bestDelta); 

输出

Closest ratio: 16/9
Ratio        : 1.7777777777777777
Inaccurate by: 2.2222222222234578E-4

更新:替代算法

我刚刚想到了一种替代算法,它试图接近近似值。当然,它仍然不是很有效......

double bestDelta = Double.MAX_VALUE;
int i = 1;
int j = 1;
int bestI = 0;
int bestJ = 0;

for (int iterations = 0; iterations < 100; iterations++) {
  double delta = (double) i / (double) j - ratio;

  // Optionally, quit here if delta is "close enough" to zero
  if (delta < 0) i++;
  else j++;

  double newDelta = Math.abs((double) i / (double) j - ratio);
  if (newDelta < bestDelta) {
    bestDelta = newDelta;
    bestI = i;
    bestJ = j;
  }
}

System.out.println("Closest ratio: " + bestI + "/" + bestJ);
System.out.println("Ratio        : " + ((double) bestI / (double) bestJ));
System.out.println("Inaccurate by: " + bestDelta);

输出相同

我是否应该偶然发现一个有效的算法,我会在这里发布: - )

答案 1 :(得分:8)

这通常是不可能的,因为double可能不代表实际(精确)分数。您将不得不依赖于其他答案中建议的启发式或暴力。

如果你有确切的decimal expansion和句号,你可以解决它。

这是笔和纸的方式:

  1. 假设您从1.77777...开始(这是16/9,但我们假设我们不知道)

  2. 您注意到句点为7(一位数),因此您乘以10(即将小数点向右移动一步):

    10n = 17.77777...
    
  3. 您现在可以通过计算10n - n

    取消重复部分
    10n - n = 17.77777... - 1.77777... = 16
    
  4. 解决n收益n = 16/9

  5. 将其转换为代码需要你弄清楚十进制扩展周期的开始和长度,这本身就是一个讨厌的问题,因为这个数字通常看起来像0.16666667

答案 2 :(得分:7)

这是一个非常晚的回复,但我用一种更容易的方式解决了这个问题,我知道其他人会很感激。

我假设您已经知道屏幕分辨率,因为您知道宽高比(十进制等效值)。您可以通过求解屏幕宽度和高度之间的最大公因子来找到纵横比(整数:整数)。

public int greatestCommonFactor(int width, int height) {
    return (height == 0) ? width : greatestCommonFactor(height, width % height);
}

这将返回屏幕宽度和高度之间的最大公因数。要查找实际宽高比,只需将屏幕宽度和高度除以最大公因子即可。所以......

int screenWidth = 1920;
int screenHeight = 1080;

int factor = greatestCommonFactor(screenWidth, screenHeight);

int widthRatio = screenWidth / factor;
int heightRatio = screenHeight / factor;

System.out.println("Resolution: " + screenWidth + "x" + screenHeight;
System.out.println("Aspect Ratio: " + widthRatio + ":" + heightRatio;
System.out.println("Decimal Equivalent: " + widthRatio / heightRatio;

输出:

Resolution: 1920x1080
Aspect Ratio: 16:9
Decimal Equivalent: 1.7777779

希望这有帮助。

注意:这对某些分辨率不起作用。评论包含更多信息。

答案 3 :(得分:3)

这是Scala中的一个实现,它根据Farey序列找到Best rational approximation。此算法由@AakashM建议,并由John D. Cook's Python implementationDavid Weber's C++ modification翻译。

/**
 * Calculates the `Best rational approximation` based on the Farey sequence.
 *
 * Translated from John D. Cook's Python implementation and David
 * Weber's C++ modification.
 *
 * @param x A value to be approximated by two integers.
 * @param eps The required precision such that abs(x-a/b) < eps. Eps > 0.
 * @param n The maximum size of the numerator allowed.
 * @return The best rational approximation for x.
 */
def farey(x: Double, eps: Double, n: Int): (Int, Int) = {

  @tailrec
  def iterate(a: Int, b: Int, c: Int, d: Int): (Int, Int) = {
    if (b <= n && d <= n) {
      val mediant = (a + c).toDouble / (b + d).toDouble
      if (Math.abs(x - mediant) < eps) {
        if (b + d <= n) {
          (a + c) -> (b + d)
        } else if (d > b) {
          c -> d
        } else {
          a -> b
        }
      } else if (x > mediant) {
        iterate(a + c, b + d, c, d)
      } else {
        iterate(a, b, a + c, b + d)
      }
    }
    else if (b > n) c -> d
    else a -> b
  }

  iterate(0, 1, 1, 0)
}

我创建了一个gist,其中也包含了一些测试。

答案 4 :(得分:1)

这是一个线性方程。一般来说,线性方程中不能有两个未知数

答案 5 :(得分:1)

实际上,a/b形式的所有因子都表示为有限比率或无限但周期比率(提供ab是整数)。不过,时期可能相当大。您可以尝试检测它并找到确切的比例,如果周期至少比双精度小一半。或者你可以尝试做出最好的猜测。

答案 6 :(得分:0)

宽高比可以是实数(例如1.85:1),所以我担心不可能从因素中“猜测”宽高比。

但是可能有10种常用的宽高比。您可以轻松制作因子宽高比表。

答案 7 :(得分:0)

如果有人想根据纵横比和对角线计算电视的高度和宽度,请使用以下代码。

public void printTvHeightAndWidth(){
            int widhtRatio = 16;
            int heightRatio = 9;
            int diagonal = 88;

            double tvHeight = (heightRatio * diagonal) / Math.sqrt(widhtRatio * widhtRatio + heightRatio * heightRatio);
            double tvWidth = (widhtRatio * diagonal) / Math.sqrt(widhtRatio * widhtRatio + heightRatio * heightRatio);
            DecimalFormat df = new DecimalFormat("#.##");
            System.out.println("W = " + df.format(tvWidth) + " H = " + df.format(tvHeight));

        }