How to calculate the aspect ratio by a given factor?
How do I calculate the aspect ratio (formatted as integer:integer
) by a given factor?
For example, aspect ratio 16:9 has a factor of 1.778, because 16 / 9 = 1.778. But how can I find the ratio by that factor? So
Dimension getAspectRatio(double factor) {开发者_JAVA技巧
...
}
public static void main(String[] arguments) {
Dimension d = getAspectRatio(16d / 9d);
System.out.println(d.width + ":" + d.height);
}
should return
16:9
This is an extremely late reply, but I have solved this using a much easier way and I know other's would appreciate it.
I'm assuming that you already know the screen resolution since you know the aspect ratio (decimal equivalent). You can find the aspect ratio (integer:integer) by solving for the greatest common factor between the screen width and height.
public int greatestCommonFactor(int width, int height) {
return (height == 0) ? width : greatestCommonFactor(height, width % height);
}
This will return the greatest common factor between the screen width and height. To find the actual aspect ratio, you just divide the screen width and height by the greatest common factor. So...
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;
This outputs:
Resolution: 1920x1080
Aspect Ratio: 16:9
Decimal Equivalent: 1.7777779
Hope this helps.
Note: This won't work for some resolutions. Comments contain more info.
Disclaimer: These algorithms are silly and inefficient. I'm sure there's a better one...
A silly, straightforward (not very efficient) algorithm to find an approximation is this:
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);
Output.
Closest ratio: 16/9
Ratio : 1.7777777777777777
Inaccurate by: 2.2222222222234578E-4
Update: Alternative algorithm
I've just thought of an alternative algorithm, which tries to close in on the approximation. Of course, it's still not very efficient...
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);
The output is the same
Should I stumble upon an efficient algorithm, I'll post it here :-)
This is not possible in general, since a double may not represent the actual (exact) fraction. You'll have to rely on heuristics or brute force as suggested in the other answers.
If you had the exact decimal expansion and period you could solve it though.
Here's the pen and paper way:
Suppose you start with
1.77777...
(which is 16/9 but let's assume that we didn't know that)You note that the period is
7
(one digit) so you multiply by 10 (i.e. move the decimal point one step to the right):10n = 17.77777...
You can now cancel out the repeating part by computing
10n - n
:10n - n = 17.77777... - 1.77777... = 16
Solving for
n
yieldsn = 16/9
Translating this to code would require you to figure out the start and length of the period of the decimal expansion which will itself be a nasty problem, as the number could typically look something like 0.16666667
.
Here is an implementation in Scala which finds the Best rational approximation
based on the Farey sequence. This algorithm was suggested by @AakashM and it's translated from John D. Cook's Python implementation and 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)
}
I've created a gist which contains also some tests.
This is a linear equation. In general you can't have two unknowns in a linear equation.
Actually, all the factors of form a/b
are presented as finite ratios or infinite but periodic ratios (provided a
and b
are integers). Period can be pretty big, though. You could try to detect it and find exact ratio if period is, at least, half less than double precision. Or you could try to make best guess.
Aspect ratio can be be real number (1.85:1 for example), so I'm afraid its impossible to "guess" aspect ration from factor.
But there are maybe 10 common used aspect ratios. You could easy make factor-aspect ratio table.
If someone wants to calculate the tv height and width based on aspect ratio and diagonal use the below code.
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));
}
精彩评论