6

如何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
4

8 回答 8

12

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

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

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

希望这可以帮助。

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

于 2015-01-03T10:41:00.303 回答
10

免责声明:这些算法愚蠢且效率低下。我敢肯定还有更好的...

寻找近似值的一种愚蠢、直接(不是很有效)的算法是:

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);

输出是一样的

如果我偶然发现一个有效的算法,我会在这里发布:-)

于 2011-09-16T08:54:57.787 回答
7

这通常是不可能的,因为双精度数可能不代表实际(精确)分数。正如其他答案中所建议的那样,您将不得不依赖启发式或蛮力。

如果你有确切的小数扩展和句点,你可以解决它。

这是笔和纸的方式:

  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

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

于 2011-09-16T08:50:53.823 回答
3

这是 Scala 中的一个实现,它Best rational approximation基于 Farey 序列找到 。这个算法是由@AakashM 提出的,它是从John D. Cook 的 Python implementationDavid Weber 的 C++ modify 翻译而来的。

/**
 * 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)
}

我创建了一个要点,其中还包含一些测试。

于 2015-10-14T10:42:34.907 回答
1

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

于 2011-09-16T08:54:45.950 回答
1

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

于 2011-09-16T08:58:21.387 回答
0

纵横比可以是实数(例如 1.85:1),所以恐怕不可能从因子“猜测”纵横比。

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

于 2011-09-16T08:58:46.883 回答
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));

        }
于 2019-12-21T08:20:02.167 回答