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