如何按给定因子计算宽高比(格式为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
答案 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.77777...
开始(这是16/9,但我们假设我们不知道)
您注意到句点为7
(一位数),因此您乘以10(即将小数点向右移动一步):
10n = 17.77777...
您现在可以通过计算10n - n
:
10n - n = 17.77777... - 1.77777... = 16
解决n
收益n = 16/9
将其转换为代码需要你弄清楚十进制扩展周期的开始和长度,这本身就是一个讨厌的问题,因为这个数字通常看起来像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 implementation和David 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
形式的所有因子都表示为有限比率或无限但周期比率(提供a
和b
是整数)。不过,时期可能相当大。您可以尝试检测它并找到确切的比例,如果周期至少比双精度小一半。或者你可以尝试做出最好的猜测。
答案 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));
}