Как рассчитать соотношение сторон по заданному коэффициенту? - PullRequest
4 голосов
/ 16 сентября 2011

Как рассчитать соотношение сторон (отформатированное как integer:integer) по заданному коэффициенту?

Например, соотношение сторон 16: 9 имеет коэффициент 1,777, потому что 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

Ответы [ 7 ]

9 голосов
/ 16 сентября 2011

Отказ от ответственности: эти алгоритмы глупы и неэффективны.Я уверен, что есть лучший ...

Глупый, простой (не очень эффективный) алгоритм для поиска аппроксимации таков:

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

Вывод тот же

Если я наткнусь на эффективный алгоритм, я выложу его здесь: -)

8 голосов
/ 03 января 2015

Это очень поздний ответ, но я решил это гораздо более простым способом, и я знаю, что другие оценят его.

Я предполагаю, что вы уже знаете разрешение экрана, поскольку знаете соотношение сторон (десятичный эквивалент). Вы можете найти соотношение сторон (целое число: целое), решив наибольший общий коэффициент между шириной и высотой экрана.

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

Надеюсь, это поможет.

Примечание: это не будет работать для некоторых разрешений. Комментарии содержат больше информации.

8 голосов
/ 16 сентября 2011

В целом это невозможно, поскольку двойное число может не представлять фактическую (точную) дробь.Вам придется полагаться на эвристику или грубую силу, как предложено в других ответах.

Если у вас было точное десятичное расширение и период, который вы могли бы решить, хотя.

Вот способ с ручкой и бумагой:

  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.

3 голосов
/ 14 октября 2015

Вот реализация в Scala, которая находит Best rational approximation на основе последовательности Фэри.Этот алгоритм был предложен @AakashM и переведен с Реализация Python Джона Д. Кука и Модификация C ++ Дэвида Вебера .

/**
 * 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 , который также содержит несколько тестов.

1 голос
/ 16 сентября 2011

На самом деле все факторы формы a/b представлены в виде конечных или бесконечных, но периодических отношений (при условии, что a и b являются целыми числами)Период может быть довольно большим, хотя.Вы можете попытаться обнаружить его и найти точное соотношение, если период, по крайней мере, вдвое меньше двойной точности.Или вы можете попытаться угадать.

1 голос
/ 16 сентября 2011

Это линейное уравнение. В общем случае вы не можете иметь двух неизвестных в линейном уравнении.

0 голосов
/ 16 сентября 2011

Соотношение сторон может быть действительным числом (например, 1,85: 1), поэтому я боюсь, что невозможно «угадать» соотношение сторон по фактору.

Но, возможно, существует 10 часто используемых форматов изображения. Вы можете легко составить таблицу коэффициентов и коэффициентов.

...