繁体   English   中英

Ruby Binary Search在某些时候没有注意到数组中的值

[英]Ruby Binary Search not noticing values already in array some of the time

我正在将项目添加到数组。 要添加它们,我让我的应用程序二进制搜索我当前的数组(如果有的话)-它不添加该项。 如果项目不存在,则会添加该项目:

while (line = fileObj.gets)

  itemD = line.split(" ")
  number = itemD.at(0)
  name = itemD.at(1)

  if (search(items, number) == -1)
    # Doesn't add if item already exists
    puts "Already have: #{number} - #{name}"
  else
    # Adds a new item to the list
    items << Item.new(number, name)
  end
end

在上面,我的循环从文件读取输入,获取数字和名称。 然后搜索号码。 上面的代码工作得很好,所以下面的代码(二进制搜索)无法正常工作:

def search(array, key)
  min = 0
  max = array.length-1
  mid = 0

  while(min <= max)
    mid = lo + ((min - max)/2)

    if array[mid].number == key
      return -1
    elsif array[mid].number < key
      min = mid + 1
    else
      max = mid - 1
    end
  end

  puts "#{key} not found in array"
end

使用基本测试用例时,我注意到了这个问题:

58        Item 1
17        Item 2
58        Item 3
76        Item 4
06        Item 5
08        Item 6
17        Item 7
21        Item 8
76        Item 4
76        Item 4
00        Item 9
49        Item 10
40        Item 11
79        Item 12
31        Item 13

尽管名称不同,但它不会添加项目3(这很好,因为该数字已存在)。 然后到达第二项4。将其添加,然后到达第三项4,并注意到它在数组中,并且没有添加。 我的输出如下(以帮助跟踪问题):

58 not found in array
17 not found in array
Already have: 58 - Item 3
76 nfia
06 nfia
08 nfia
17 nfia
21 nfia
76 nfia (WRONG)
Already have: 76 - Item 4 (CORRECT)
00 nfia
49 nfia
40 nfia
79 nfia
31 nfia

接下来,我多次仅创建了一个测试用例:

58        Item 1
58        Item 1
58        Item 1
58        Item 1
58        Item 1

它正确地添加了第一个,并注意到了下四个(并且正确地没有添加它们)。 因此,很明显,如果该数字出现在列表的后面而不是刚添加后的数字,则还有更多工作要做。 所以接下来我测试了:

58        Item 1
08        Item 6
58        Item 1
08        Item 6
58        Item 1
08        Item 6
58        Item 1
08        Item 6
58        Item 1
08        Item 6

这显示了输出所指的内容,但它是一个有趣的迭代:

58 nfia
08 nfia
Already have 58
08 nfia (WRONG)
58 nfia (WRONG)
already have
already have
already have
already have
already have

有人可以帮助我为什么有时会注意到我数组中的元素,但不是每次都应该注意到它。 感谢您的协助,不胜感激!

除了提到Ruby提供了Array#bsearch方法来执行二进制搜索之外,我将让其他人解决您的二进制搜索方法的问题。 我想提出一种更像Ruby的方式来执行任务。

require 'set'

def process_file(fname)
  s = Set.new
  items = []
  File.foreach(fname) do |line|
    pair = line.split
    if s.add?(pair)
      items << Item.new(*pair)
    else
      puts "Already have: %s - %s" % pair
    end
  end
  items
end

让我们创建一个测试文件。

str =<<_
Humpty Dumpty
sat on
a wall.
Humpty Dumpty
had a
great fall.
_

FName = "test"

File.write(FName, str)
  #=> 61

让我们检查一下。

puts File.read(FName)
Humpty Dumpty
sat on
a wall.
Humpty Dumpty
had a
great fall.

我们需要一个类Item

class Item
  def initialize(*pair)
    @pair = pair
  end
end

我们准备出发了。

process_file(FName)
  Already have: Humpty - Dumpty
  #=> [#<Item:0x007f978404e2f0 @pair=["Humpty", "Dumpty"]>,
  #    #<Item:0x007f978404c810 @pair=["sat", "on"]>,
  #    #<Item:0x007f9784046370 @pair=["a", "wall."]>,
  #    #<Item:0x007f978403ced8 @pair=["had", "a"]>,
  #    #<Item:0x007f9784037e88 @pair=["great", "fall."]>] 

参见Set :: newIO :: foreachString#splitSet#add? IO方法(例如foreach )通常在File类(它是IO的子类)上调用。

上面的代码工作得很好,所以下面的代码(二进制搜索)无法正常工作

该语句是错误的:上面的代码不能 “正常工作”。 特别是,二进制搜索需要对序列进行排序,因此您需要在调用search之前对其进行排序,或者确保将新项插入正确的位置,以便始终对数组进行排序。 但是,您只需将项目粘贴到数组的末尾,而不管其number为何,就永远不会对数组进行排序。

还有另一个错误,就是您的number引用的对象实际上是一个String而不是数字。 这可能只是变量的错误命名,但是从代码的其余部分看来,它实际上是一个数字。 特别是,我不确定您实际上是否打算将11小于2 ,这是字符串的情况。

您的search方法中至少还有一个错误:在任何地方都没有定义lo

假设您的意思是min ,公式仍然是错误的,应该是min + ((max - min) / 2) ,而不是相反。 但是,实际上,整个体操并不是必需的。 仅在破坏了算术的语言中才需要这样做。 Ruby完全有能力将两个整数相加而不会出错,因此您可以做(min + max) / 2

另一个问题是, search方法的返回值实际上没有任何意义。 首先,返回值对于实际获取项目是无用的。 我希望search方法可以返回该项目,也可以(如果是可索引序列,如数组)返回该项目。 如果该项存在,则search方法将返回-1否则将返回nil 因此,换句话说,它仅告诉您该项目是否存在。 这不是非常有用:想象一下,如果您将搜索查询键入Google,它只会返回“是的,您正在搜索的内容存在。但是我没有告诉您在哪里!” 或者,您要求某人帮助您搜索意外掉落的隐形眼镜,然后他们告诉您“找到了!”。 然后就走开。

通常,二进制搜索有两种形式:一种返回元素的索引(如果存在),而另一种(例如-1nilNULL或其他特殊指定值)则告诉您该项目不存在。 另一种形式总是返回项目在序列中所属位置的索引。 由于二进制搜索要求对数组进行排序,但是对数组进行排序没有意义(您可能使用二进制搜索,因为它只需要O(log n)比较,而不是线性搜索的O(n),但是排序可以进行O(n * log n)比较(或基于非比较排序的O(n),因为您的键是数字),因此总体上比简单的线性搜索要慢),您必须插入项在序列中的正确位置,这意味着您需要一种二进制搜索形式,告诉您该项所属的索引,即使该项不存在( 尤其是不存在),也可以在右侧插入地点)。

不幸的是,修复所有这些错误和设计错误将超出简单的堆栈溢出答案的范围。

我想提出一个替代方案:设计程序时最重要的步骤之一就是确定要使用的数据结构。 在这种情况下,您希望数组中没有重复项,这似乎表明您实际上根本不需要数组,而是一个集合。 也许您想要一个排序的集合,但是从您的描述和代码来看,它实际上看起来并不像您需要的那个属性。

所以,我建议这样的事情:

class Item < Struct.new(:number, :name)
  def eql?(other)
    number.eql?(other.number)
  end

  def hash
    number.hash
  end

  # the following are only needed for a sorted set

  def <=>(other)
    number <=> other.number
  end

  include Comparable
end

require 'set'

items = File.foreach('filename.txt').
  map {|line| number, name = line.chomp.split(nil, 2); Item.new(number.to_i, name) }.
  to_set
#=> #<Set: {#<struct Item number=58, name="Item 1">,
            #<struct Item number=17, name="Item 2">,
            #<struct Item number=76, name="Item 4">,
            #<struct Item number=6,  name="Item 5">,
            #<struct Item number=8,  name="Item 6">,
            #<struct Item number=21, name="Item 8">,
            #<struct Item number=0,  name="Item 9">,
            #<struct Item number=49, name="Item 10">,
            #<struct Item number=40, name="Item 11">,
            #<struct Item number=79, name="Item 12">,
            #<struct Item number=31, name="Item 13">}>

# if you want a sorted set instead:

items = SortedSet.new(File.foreach(filename.txt)) {|line| 
  number, name = line.chomp.split(nil, 2); Item.new(number.to_i, name)
}
#=> #<SortedSet: {#<struct Item number=0,  name="Item 9">,
                  #<struct Item number=6,  name="Item 5">,
                  #<struct Item number=8,  name="Item 6">,
                  #<struct Item number=17, name="Item 2">,
                  #<struct Item number=21, name="Item 8">,
                  #<struct Item number=31, name="Item 13">,
                  #<struct Item number=40, name="Item 11">,
                  #<struct Item number=49, name="Item 10">,
                  #<struct Item number=58, name="Item 1">,
                  #<struct Item number=76, name="Item 4">,
                  #<struct Item number=79, name="Item 12">}>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM