Java 如何根据给定的系数计算纵横比?
如何根据给定因子计算纵横比(格式为Java 如何根据给定的系数计算纵横比?,java,math,aspect-ratio,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.p
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
这在一般情况下是不可能的,因为双精度可能不代表实际(精确)分数。正如其他答案中所建议的那样,你必须依靠启发式或蛮力 如果你有确切的时间,你可以解决它 以下是用笔和纸的方式:
1.77777…
(16/9,但假设我们不知道)7
(一位数字),因此您需要乘以10(即将小数点向右移动一步):
10n-n
来取消重复部分:
10n - n = 17.77777... - 1.77777... = 16
n
得到n=16/9
将此转换为代码需要您计算十进制展开周期的开始和长度,这本身就是一个棘手的问题,因为数字通常看起来像
0.16666667
这是一个线性方程。一般来说,一个线性方程中不能有两个未知数。免责声明:这些算法既愚蠢又低效。我相信有一个更好的。。。
寻找近似值的愚蠢、直接(不是很有效)算法如下:
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);
更新:替代算法
我刚刚想到了另一种算法,它试图接近近似值。当然,它仍然不是很有效
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);
double bestDelta=double.MAX_值;
int i=1;
int j=1;
int-bestI=0;
int-bestJ=0;
对于(int迭代次数=0;迭代次数<100;迭代次数++){
双δ=(双)i/(双)j-比率;
//或者,如果增量“足够接近”为零,则退出此处
if(delta<0)i++;
else-j++;
double newDelta=数学绝对值((double)i/(double)j-比率);
if(新增量<最佳增量){
bestDelta=newDelta;
bestI=i;
最佳j=j;
}
}
系统输出打印项次(“最接近比率:“+bestI+”/“+bestJ”);
System.out.println(“比率:”+((双)最佳/(双)最佳j));
System.out.println(“不准确者:“+bestDelta”);
输出是相同的
如果我偶然发现了一个有效的算法,我会把它贴在这里:-)实际上,形式
a/b
的所有因子都表示为有限比率或无限但周期比率(前提是a
和b
都是整数)。不过,周期可能相当大。若周期至少比两倍精度低一半,你们可以试着检测它并找到精确的比率。或者你可以试着做出最好的猜测。纵横比可以是实数(例如1.85:1),所以我恐怕不可能从因子中“猜测”纵横比
但可能有10种常用的纵横比。你可以很容易地制作因子纵横比表。这是一个非常晚的答复,但我已经用一种更简单的方法解决了这个问题,我知道其他人会很感激的 我假设您已经知道屏幕分辨率,因为您知道纵横比(十进制等效值)。通过求解屏幕宽度和高度之间的最大公因数,可以找到纵横比(整数:整数)
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
希望这有帮助
注意:这对某些分辨率不起作用。注释包含更多信息。这里是Scala中的一个实现,它根据Farey序列查找
最佳有理近似值。该算法由@AakashM提出,并由和翻译而来
/**
*根据Farey序列计算“最佳有理近似”。
*
*翻译自John D.Cook的Python实现和David
* Weber的C++修改。
*
*@param x由两个整数近似的值。
*@param eps所需的精度,以使abs(x-a/b)0。
*@param n允许的最大分子大小。
*@返回x的最佳有理近似值。
*/
def-farey(x:Double,eps:Double,n:Int):(Int,Int)={
@泰勒克
def迭代(a:Int,b:Int,c:Int,d:Int):(Int,Int)={
if(b)d
}否则{
a->b
}
}else if(x>mediant){
迭代(a+c,b+d,c,d)
}否则{
迭代(a,b,a+c,b+d)
}
}
否则,如果(b>n)c->d
其他a->b
}
迭代(0,1,1,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));
}
检查常用纵横比Nope!这本质上是一个简单的查找近似GCD问题。您可以通过查找输入的近似值来获得更好的效果。我认为应该用另一种方法更好地评估近似值的质量:它是delta/minstep
,其中minstep是1/分母。这样我们就避免了选择1778/1000作为更好的近似值。事实上,对于给定的分母,我们至少与minstep
一样精确,因此我们在明显的精度范围内检查近似值有多好。正如aioobe所说,由于四舍五入,您将无法获得准确的比率,但@lukas可能是您的最佳解决方案。尽管如果使用时髦的因子,例如5.941148,它是101/17(我刚刚选择了一个素数和一个超过100的数字,所以这将在循环之外-不是一个实际的显示大小,只是说明一个点)。而且你不需要循环分子:你可以只选择分母并计算出最佳匹配的分子(a)
Resolution: 1920x1080
Aspect Ratio: 16:9
Decimal Equivalent: 1.7777779
/**
* 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)
}
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));
}