Указать нелинейный диапазон на линейный и обратно? - PullRequest
0 голосов
/ 02 июня 2009

Приветствие, я пытаюсь решить следующую загадку:

У меня есть список линейных диапазонов, которые представляют один большой диапазон.

                                          X'
 100    200 300    400 500    600 700     |       900    (X)
|----------|----------|----------|--------+----------|
 0                                        |       100    (Y)
                                          Y'

X состоит из следующих диапазонов ( четные и круглые числа являются просто примерами для простоты понимания, они могут быть любыми, без пропорций здесь ):

  • от 100 до 200
  • от 300 до 400
  • от 500 до 600
  • от 700 до 900

С другой стороны, у Y есть только один диапазон:

  • от 0 до 100

Оба X и Y имеют одинаковую длину, только разные единицы. Допустим, один - доллары, а другой - проценты (или любые другие аналогично не связанные единицы). Итак, Y'0 == X'100 и Y'100 == X'900.

Вопрос : Для любой точки в Y, что является эквивалентной точкой в ​​X и наоборот, для данной точки в X - что это за Y?

Это типичная математическая задача? У него есть имя? Все, что меня направит в правильном направлении, поможет.

Спасибо.

Вот решение, основанное на ответе Игоря Кривокона . Мне пришлось убрать умножение на два, чтобы оно заработало.

input_range = [
  [100, 200],
  [300, 400],
  [500, 600],
  [700, 800]
]

range_sum = 0
lookup_list = []

input_range.each do |range_start, range_end|
  lookup_list.push [ range_start, range_sum ]
  range_sum += range_end - range_start
end

def get_percent(value, lookup_list, range_sum)
  result = 0

  lookup_list.reverse.each do |range_start, cummulative_sum|
    if value >= range_start
      result = value + cummulative_sum - range_start
      break
    end
  end

  return result.to_f / range_sum.to_f
end

def get_value(percent, lookup_list, range_sum)
  result = 0
  value = range_sum * percent

  lookup_list.reverse.each do |range_start, cummulative_sum|
    if value >= cummulative_sum
      result = value - cummulative_sum + range_start
      break
    end
  end

  return result.to_f
end

puts "Input range: #{input_range.inspect}"
puts "Continous range sum: #{range_sum}"
puts "Lookup list: #{lookup_list.inspect}"

input_range.each do |range|
  range.each do |start_or_end|
    puts "#{start_or_end} is at " + get_percent(start_or_end, lookup_list, range_sum).to_s
  end
end

[ 0, 0.25, 0.5, 0.75, 1 ].each do |percent|
  puts "#{percent} is at " + get_value(percent, lookup_list, range_sum).to_s
end

$> ruby ​​range.rb

Input range: [[100, 200], [300, 400], [500, 600], [700, 800]]
Continous range sum: 400
Lookup list: [[100, 0], [300, 100], [500, 200], [700, 300]]
100 is at 0.0
200 is at 0.25
300 is at 0.25
400 is at 0.5
500 is at 0.5
600 is at 0.75
700 is at 0.75
800 is at 1.0
0 is at 100.0
0.25 is at 300.0
0.5 is at 500.0
0.75 is at 700.0
1 is at 800.0

$> ruby ​​range.rb

Input range: [[100, 200], [300, 400], [500, 600], [700, 1000]]
Continous range sum: 600
Lookup list: [[100, 0], [300, 100], [500, 200], [700, 300]]
100 is at 0.0
200 is at 0.166666666666667
300 is at 0.166666666666667
400 is at 0.333333333333333
500 is at 0.333333333333333
600 is at 0.5
700 is at 0.5
1000 is at 1.0
0 is at 100.0
0.25 is at 350.0
0.5 is at 700.0
0.75 is at 850.0
1 is at 1000.0

Кажется, он работает с любым диапазоном, который я ему даю, туда и обратно.

Ответы [ 5 ]

2 голосов
/ 02 июня 2009

Сколько у вас диапазонов? Является ли приемлемым, что алгоритм O (количество диапазонов)?

Если это так, ниже приведено описание алгоритма. Позвольте мне объяснить это на вашем (оригинальном) примере.

 100    200 300    400 500    600 700    800
|----------|----------|----------|----------|
 0%                                     100%

1) То, что вы делаете, это сопоставляете значение X в диапазоне A (100-800) со значением Y в непрерывном диапазоне B (0-399) (так как общее количество элементов в вашем диапазоне 400). Тогда легко изменить положение в B на проценты, я опущу эту часть.

2) Создайте список записей, где каждая запись представляет одно отображение диапазона.

struct RangeRecord {
  int start_in_a;
  int start_in_b;
};

В вашем случае вы получите следующий список:

{100, 0}, {300, 100}, {500, 200}, {700, 300}

3) Когда вам нужно сопоставить число X от A до B, вы перебираете список, чтобы найти первую запись с start_in_a <= X. Тогда ваше значение Y равно </p>

Y = X + start_in_b - start_in_a;

4) Алгоритм является симметричным, вы просто просматриваете список, чтобы найти первую запись с start_in_b <= Y, а затем </p>

X = Y + start_in_a - start_in_b.

Примечание 1. В целях проверки ошибок вы также можете сохранить размер диапазона в RangeRecord.

Примечание 2. Если O (количество диапазонов) недостаточно, сохраняйте записи в виде дерева вместо списка. Тогда вам понадобится O (log (количество диапазонов)) операций,

0 голосов
/ 02 июня 2009

У вас есть три вещи, которые нужно отрегулировать при преобразовании из некоторого X 'в Y' и наоборот:

  1. Диапазоны начинаются в разных местах.
  2. Один из них прерывистый.
  3. Размер каждого шага различен для двух диапазонов.

Может быть полезно (по крайней мере, при разработке ваших решений) рассмотреть аналогичный диапазон Z, который является диапазоном от 0 до 503 и имеет взаимно-однозначное отображение с 504 возможными значениями в X. То есть для каждый разрыв, если значение X больше верхнего конца разрыва, вычитают 99 (размер разрыва). Тогда X'100 = Z'0, X'200 = Z'100, X'300 = Z'101, X'400 = Z'201, X'500 = Z'202 и т. Д. Введение Z-диапазона разрешает проблемы 1 и 2 в списке выше.

Чтобы преобразовать из Z в Y, вы просто умножаете на 101/504, что переводит Z в Y.

0 голосов
/ 02 июня 2009

Это не совсем решаемо, потому что проблема не указана. Даже для одних и тех же диапазонов могут быть разные ползунки, подобные этому:

1  100 101       1000
|-----|-----------|

1        100 101 1000
|-----------|-----|

Для каждого диапазона, например [1..100], вам необходимо знать, каким процентным точкам на ползунке он соответствует. В приведенных выше примерах это может быть что-то вроде [0%..33%] или [0%..66%]. Получив эту информацию, легко определить, в каком из диапазонов и в каком положении этого диапазона находится данная точка данных и какому значению она соответствует.

0 голосов
/ 02 июня 2009

Предполагая, что вы подразумеваете кусочно-линейное расположение, вы можете найти X по:

X = 4*Y + 100*int(1 + Y/25.)

и наоборот для Y:

X2 = int(X/100.)
X3 = X2-int(X2/2.)
Y = (X-100*X3)/4.

edit: Это решение работает для исходного диапазона, который вы дали:

 100    200 300    400 500    600 700    800
|----------|----------|----------|----------|
 0%                                     100%

И, конечно, обратная формула верна только для действительных значений X.

Вот фигура двух кривых. Зеленый - ваша исходная спецификация, а синий - обратная кривая (опять-таки, действительная только для действительных значений x) альтернативный текст http://img523.imageshack.us/img523/8858/66945008.png

0 голосов
/ 02 июня 2009

Скажем, у вас есть один диапазон (a, b) и другой (c, d). Теперь у вас есть номер i, для которого a Скажите, что соответствующая точка в другом диапазоне - это i '. Тогда,

i' = (i - a) / (b - a) * (d - c) + c

Термин, который вы ищете, - это масштабирование и перевод.

...