Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何有效地确定字符串中100个字符的每个块中特定字符的百分比?_Ruby_Algorithm - Fatal编程技术网

Ruby 如何有效地确定字符串中100个字符的每个块中特定字符的百分比?

Ruby 如何有效地确定字符串中100个字符的每个块中特定字符的百分比?,ruby,algorithm,Ruby,Algorithm,我试图计算任意长度字符串的每个100块子字符串中特定字符的百分比。我有一个如下所示的工作版本,但给定的字符串可能非常长—10秒,数千到数百万个字符 字符串将由不超过8个不同的字符组成:A、B、C、D、E、F、G和H 我需要扫描每个100个字符的块,并确定该块中给定字符的百分比。如果百分比大于确定的量,则记录块索引。我发现很难解释什么是“100字符块”。我不需要将字符串分成100个字符的块,我需要从每个字符开始读取接下来的99个字符,然后对每个字符重复直到结束。比如读[0..99]、[1..100

我试图计算任意长度字符串的每个100块子字符串中特定字符的百分比。我有一个如下所示的工作版本,但给定的字符串可能非常长—10秒,数千到数百万个字符

字符串将由不超过8个不同的字符组成:A、B、C、D、E、F、G和H

我需要扫描每个100个字符的块,并确定该块中给定字符的百分比。如果百分比大于确定的量,则记录块索引。我发现很难解释什么是“100字符块”。我不需要将字符串分成100个字符的块,我需要从每个字符开始读取接下来的99个字符,然后对每个字符重复直到结束。比如读[0..99]、[1..100]、[2..101]、[3..102]、[4..103]等等

我目前正在强制计算,但速度相当慢。有没有一种聪明的方法可以让这更有效

def calculate_percentage_errors full_string, searched_character, percentage_limit 
# full_string:        ABCDGFGEDCBADDEGDCGGBCDEEFGAAAC.......
# searched_character: A
# percentage_limit:   0.5

n = 0 
error_index = []
while n < (full_string.length - 99) do
  #grab the string 1..100, 2..101 .... 
  sub_string =  full_string[n..(n+99)] 

  # determine the number of characters in the string
  character_count = (100 - sub_string.gsub(searched_character, '').length)

  if (character_count/100.0) > percentage_limit
    # record the index if percentage exceeds limit
    error_index << [(n+1),(n+100)]
  end

  n += 1
end

return error_index
end

如果您必须在100个字符的同一块中搜索多个不同的字符,您可能需要一次搜索:

def chars_in_block(block)
  result = Hash.new(0)
  block.each_char { |c| result[c] += 1 }
  result
end

这将返回一个散列,然后可以根据您的规则进行过滤。它将保证您只通过一次。

使用上一块的计数。最多更改2次。让我举个例子。如果在块2..101中有5次A出现,并且您想要计算3..102的计数,您可以简单地检查位置2处是否有A,位置102处是否有A。例如,如果102处有A,而不是2处,则计数将为6。你需要再看三个案子。我相信用这个会快得多

以下是一些示例代码:

def calculate_percentage_errors full_string, searched_character, percentage_limit                                                                                         
  count = full_string[0..99].count(searched_character)                                                                                                                    
  error_index = []                                                                                                                                                        
  error_index << full_string[0..99] if count / 100.0 > percentage_limit                                                                                                   

  1.upto(full_string.length - 100).each do |index|                                                                                                                        
    count -= 1 if searched_character == full_string[index - 1]                                                                                                            
    count += 1 if searched_character == full_string[index + 99]                                                                                                           

    error_index << full_string[index, index + 99] if count / 100.0 > percentage_limit                                                                                     
  end                                                                                                                                                                     

  error_index                                                                                                                                                             
end 

要拥有100个字符的数组窗口,可以使用from Enumerable mixin。所以不是

while n < (full_string.length - 99) do
  sub_string =  full_string[n..(n+99)] 

  # .. your code ..

  n += 1
end
因为它只使用迭代器,所以它应该更高效、更快

如果需要错误索引的索引,可以使用from枚举器类

这是您的代码重写

def calculate_percentage_errors(full_string, searched_character, percentage_limit)
  # full_string:        ABCDGFGEDCBADDEGDCGGBCDEEFGAAAC.......
  # searched_character: A
  # percentage_limit:   0.5

  error_index = []
  threshold = (percentage_limit * 100)
  count = nil
  full_string.each_char.each_cons(100).with_index do |sub_string, index|
    # count searched characters the first time, then adjust as characters are read
    if count.nil?
      count = sub_string.count(searched_character)
    else
      count += 1 if sub_string.last == searched_character
    end

    # record the index if percentage exceeds limit
    error_index << [index + 1, index + 100] if count > threshold

    # adjust count
    count -= 1 if sub_string.first == searched_character
  end
  return error_index
end

编辑:更新答案,只对每个字符计数2次,正如@Max建议的那样,您不必在每个索引位置进行检查

假设错误限制完整字符串长度乘以百分比限制为n,并且在位置[i,100]处的子字符串中得到字符A的m个计数。如果m小于n,则可以跳过该索引,以便下一个要检查的索引为[i+n-m,100],因为对于任何j:

i [j,100]中A的最大计数是m+j-i,当[i…j]中没有字符是A且[i+100…j+100]中的所有字符都是A时,会发生这种情况。从1开始

m+j-i 我们知道[j,100]中A的计数小于n。 考虑到这一事实,该算法可以改进如下:

def calculate_percentage_errors full_string, searched_character, percentage_limit 
  limit = (full_string.length * percentage_limit / 100.0).to_i
  error_index = []
  i = 0
  while i < (full_string.length - 99) do
    margin = limit - full_string[i, 100].count(searched_character)
    if margin > 0
      i += margin
    else
      error_index << [i + 1, i + 100]
      i += 1
    end
  end
  error_index
end
使用每个字符和索引在字符离开块时向后查看:

def calc_errors string, char, threshold
  errors = []
  count = 0

  string.each_char.with_index do |c, i|
    count += 1 if c == char
    count -= 1 if i > 99 and string[i - 100] == char
    if i >= 99
      if count > threshold
        errors << [i - 99, i]
      end
    end
  end

  errors
end

与其他答案不同,它可以访问100次字符,此算法只访问每个字符两次:一次是在进入块时,一次是在离开块时。

请将此视为扩展注释。请不要投票;反对票勉强接受。这只是@Ivaylo建议的算法的一种实现方式

编辑:就在我即将发布的时候,我看到@Ivaylo已经实现了。无论如何,我会把这篇文章作为另一种表述发表,但同样,请把它当作对他的回答的评论

代码


@伊瓦洛佩特罗夫:这应该是一个答案,而不是评论!很好的建议,Ivaylo,但在您提供代码之前,不要使用+1。这将访问每个字符100次,因为块重叠。每个字符都是获得连续块的有效方法,但当块移动时,您仍然会对每个字符进行100次计数。@Max您是对的,可以避免计数。当OP正在努力阅读“100个字符块”时,我发现这是一个很好的机会来使用每个选项。@Max更新了答案。出于演示的目的,我坚持使用每种方法,但我更喜欢您的解决方案:可读性更强、更简单。与其他答案相同的问题是:full_string[I,100]。计数会完全取消滑动块的效率,因为它会重新访问前一块中的字符。啊,我只是注意到您的算法步骤不同。但是,我无法使您的代码适用于除0以外的任何百分比限制。这基本上与我的答案相同,尽管您的实现可能看起来更好:
def calc_errors string, char, threshold
  errors = []
  count = 0

  string.each_char.with_index do |c, i|
    count += 1 if c == char
    count -= 1 if i > 99 and string[i - 100] == char
    if i >= 99
      if count > threshold
        errors << [i - 99, i]
      end
    end
  end

  errors
end
def bad_blocks(str, contents, block_size, max_pct_per_block)
  nbr_blocks = str.size-block_size+1
  return nil if nbr_blocks < 1
  max_per_block = max_pct_per_block.to_f * block_size / 100.0 
  # g[c] is the number of times c appears in the first block
  g = block_size.times.with_object(Hash.new {|h,k|h[k]=0}) {|i,g|g[str[i]]+=1}

  # Enumerate blocks
  (nbr_blocks).times.with_object(Hash.new {|h,k| h[k]=[]}) do |b,h|
    contents.each_with_object([]) { |c,a| h[b] << c if g[c] > max_per_block }  
    g[str[b]]            -= 1 
    g[str[b+block_size]] += 1
  end
end
str = "ABCCDCEEAFFFGAGG"
bad_blocks(str, 'A'..'G', 5, 40)
  #=> {1=>["C"], 2=>["C"], 7=>["F"], 8=>["F"], 9=>["F"], 11=>["G"]}
bad_blocks(str, 'A'..'G', 5, 20)
  #=> {0=>["C"], 1=>["C"], 2=>["C"], 3=>["C", "E"], 4=>["E"], 5=>["E"],
  #    6=>["E", "F"], 7=>["F"], 8=>["F"], 9=>["F"], 10=>["F", "G"], 11=>["G"]}