指向线性和背面的非线性范围?

时间:2009-06-01 22:44:59

标签: algorithm range linear intervals

问候,我正在努力解决以下难题:

我有一个代表一个大范围的线性范围列表。

                                          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中是什么?

这是典型的数学问题吗?它有名字吗?任何让我朝着正确方向前进的方法都会有所帮助。

谢谢。

这是基于Igor Krivokon的答案的解决方案。我不得不将乘法移除2以使其有效。

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 ranges.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 ranges.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 个答案:

答案 0 :(得分:2)

你有多少个范围?算法是O(范围数)是否可以接受?

如果是,则以下是算法的描述。让我在你的(原创)例子中解释一下。

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

1)您要做的是将范围A(100-800)中的值X映射到连续范围B(0-399)中的值Y(因为您范围内的元素总数是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是

Y = X + start_in_b - start_in_a;

4)算法是symmettric,您只需迭代列表以找到start_in_b< = Y的第一条记录,然后

X = Y + start_in_a - start_in_b.

注意1.出于错误检查的目的,您也可以将范围大小保留在RangeRecord中。

注意2.如果O(范围数)不够好,请将记录保存为树而不是列表。您将需要O(log(范围数))操作,

答案 1 :(得分:0)

假设您有一个范围(a,b)和另一个范围(c,d)。现在你有一个数字i,其中<我<湾您可以通过减去a并除以b来对其进行“标准化” - a - 这会给出0到1之间的值。然后,您可以使用此值将此值与其他边界相反,将其转换为其他范围,用(d - c)乘以它并加上c。

说另一个范围内的对应点是i'。然后,

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

您要搜索的术语是缩放和翻译。

答案 2 :(得分:0)

这不是真的可以解决,因为问题没有明确说明。即使是相同的范围,也可以使用不同的滑块:

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

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

对于[1..100]这样的每个范围,您需要知道滑块上的百分点与其对应的方式。在上面的示例中,这可能类似于[0%..33%][0%..66%]。获得此信息后,可以轻松确定给定数据点的范围和范围的哪个位置以及它对应的值。

答案 3 :(得分:0)

假设您暗示了分段线性排列,您可以通过以下方式找到X

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

与Y相反:

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

编辑:此解决方案适用于您提供的原始范围:

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

当然,反向公式仅适用于X的有效值。

这是两条曲线的图。绿色是您的原始规格,蓝色是反向曲线(同样,仅对有效的x值有效)。 alt text http://img523.imageshack.us/img523/8858/66945008.png

答案 4 :(得分:0)

在将某些X'转换为Y'时,您需要调整三件事,反之亦然:

  1. 范围从不同的地方开始。
  2. 其中一个是不连续的。
  3. 每个步骤的大小在两个范围之间是不同的。
  4. 考虑一个类似的范围Z,它可能是有用的(至少在开发你的解决方案时),范围是0到503,并且与X中的504个可能值具有一对一的映射。也就是说,对于如果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.