2011-04-29 35 views
1

相等長度的給定兩個串使得其中的差異發生

s1 = "ACCT" 
s2 = "ATCT" 

我想找出其中存在字符串不同的位置比較相等的長度的串並注意到。所以我已經做到了。 (請建議一個更好的做法,我敢打賭應該有)

z= seq1.chars.zip(seq2.chars).each_with_index.map{|(s1,s2),index| index+1 if s1!=s2}.compact 

z是兩個字符串不同的位置數組。在這種情況下z返回2

想象一下,我添加了一個新的字符串

s3 = "AGCT" 

,我希望將它與其他人比較,看看那裏的3串不同。我們可以使用與上面相同的方法,但是這一次

s1.chars.zip(s2.chars,s3.chars) 

返回一個數組數組。給定兩個字符串,我只是比較兩個字符的平等,但隨着我添加更多的字符串,它開始變得壓倒性的,並且隨着字符串變長。

#=> [["A", "A", "A"], ["C", "T", "G"], ["C", "C", "C"], ["T", "T", "T"]] 

運行

s1.chars.zip(s2.chars,s3.chars).each_with_index.map{|item| item.uniq} 

#=> [["A"], ["C", "T", "G"], ["C"], ["T"]] 

可以幫助減少冗餘和返回是完全一樣的(大小爲1的非空的子陣列)的位置。然後,我可以打印出的大小的子陣列的索引和內容> 1.

s1.chars.zip(s2.chars,s3.chars,s4.chars).each_with_index.map{|item| item.uniq}.each_with_index.map{|a,index| [index+1,a] unless a.size== 1}.compact.map{|h| Hash[*h]} 
#=> [{2=>["C", "T", "G"]}] 

我覺得這將下滑停頓或很慢,因爲我增加字符串數量和字符串長度變長。什麼是最佳做法的其他方法? 謝謝。

+0

你可能想看看通用的差異算法:http://en.wikipedia.org/wiki/Diff#Algorithm – 2011-04-29 07:43:38

+0

[diff a ruby​​ string or array](http:// stackoverflow。 com/questions/80091/diff-a-ruby-string-or-array) – sawa 2011-05-23 08:23:25

回答

2

這就是我要開始的地方。我特意用不同的字符串,使其更容易看到的區別:

str1 = 'jackdaws love my giant sphinx of quartz' 
str2 = 'jackdaws l0ve my gi4nt sphinx 0f qu4rtz' 

爲了得到第一個字符串的字符:

str1.chars.with_index.to_a - str2.chars.with_index.to_a 
=> [["o", 10], ["a", 19], ["o", 30], ["a", 35]] 

要獲得第二個字符串的字符:

str2.chars.with_index.to_a - str1.chars.with_index.to_a 
=> [["0", 10], ["4", 19], ["0", 30], ["4", 35]] 

隨着琴絃變大,會有一點緩慢,但不會太壞。


編輯:增加了更多的信息。

如果你有一個字符串任意數量,並且需要比較他們,用Array#combination

str1 = 'ACCT' 
str2 = 'ATCT' 
str3 = 'AGCT' 

require 'pp' 

pp [str1, str2, str3].combination(2).to_a 
>> [["ACCT", "ATCT"], ["ACCT", "AGCT"], ["ATCT", "AGCT"]] 

在上面的輸出,你可以通過數組看到combination週期,返回各個n大小組合的數組元素。

pp [str1, str2, str3].combination(2).map{ |a,b| a.chars.with_index.to_a - b.chars.with_index.to_a } 
>> [[["C", 1]], [["C", 1]], [["T", 1]]] 

使用組合的輸出,您可以循環訪問數組,將所有元素相互比較。因此,在上面返回的數組中,在「ACCT」和「ATCT」對中,'C'是兩者之間的區別,位於字符串中的位置1。類似地,在「ACCT」和「AGCT」中,差異在位置1處再次爲「C」。最後對於'ATCT'和'AGCT'在位置1處爲'T'。

因爲我們已經看到了更長字符串示例代碼將返回多個更改的字符,這應該讓你非常接近。

+0

你會建議然後進行兩兩比較時給予比較嗎?假設你有3或4個字符串。 – eastafri 2011-04-29 08:03:20

+0

謝謝!八九不離十。我很感激! – eastafri 2011-04-29 08:30:42

+0

在'ruby 2.1.x'中,沒有'with_index'方法。你可以使用'each_with_index'方法來完成相同的操作。 – 2014-11-09 16:34:04

-1

你幾乎肯定不想用你自己的代碼來做這個分析。相反,您希望將其交給現有的multiple sequence alignment工具,如Clustal

我意識到這不是你的問題的答案,但我希望這是你的問題的解決方案!

+0

對不起,這可能不是我的問題的答案。首先,多重比對假設了一些替代函數和空位罰分並假設了一個進化歷史。在字符串沒有這種關係並且我不希望作出替代假設的情況下,這是錯誤的。 :) – eastafri 2011-04-29 07:57:30

+0

你總是可以使用一個替代矩陣來加權所有的替換。你仍然會有缺口罰分;如果你不想使用差距,你可以使它變得難以置信,所以不會打開任何空隙。 – 2011-04-29 22:31:16

2

解決方案1 ​​

strings = %w[ACCT ATCT AGCT] 

首先,加入字符串,併爲每個字符的所有位置的哈希值。

joined = strings.join 
positions = (0...joined.length).group_by{|i| joined[i]} 
# => {"A"=>[0, 4, 8], "C"=>[1, 2, 6, 10], "T"=>[3, 5, 7, 11], "G"=>[9]} 

然後,按每個字符串內的相應位置的索引,除去那些被重複許多次作爲字符串的數目。這部分是an algorithm that Jorg suggests的變體。

length = strings.first.length 
n = strings.length 
diff = Hash[*positions.map{|k, v| 
    [k, v.group_by{|i| i % length}.reject{|i, is| is.length == n}.keys] 
}] 

這會給類似:

diff 
# => {"A"=>[], "C"=>[1], "T"=>[1], "G"=>[1]} 

這意味着, 「A」 出現在所有的字符串相同的位置,和 「C」, 「T」 和 「G」 不同在字符串的位置1(從0開始計數)。

如果你只是想知道琴絃不同的位置,做

diff["G"] + diff["A"] + diff["C"] + diff["T"] 
# or diff["G"] + diff["A"] + diff["C"] 
# => [1] 

解決方案2

需要注意的是,通過保持指數的數組,其中兩兩比較失敗,並保持除了指數之外,s1與其餘(s2,s3,...)的比較就足夠了。

length = s1.length 
diff = [] 
[s2, s3, ...].each{|s| diff += (0...length).reject{|i| s1[i] == s[i]}} 

說明在稍微詳細

假設

s1 = 'GGGGGGGGG' 
s2 = 'GGGCGGCGG' 
s3 = 'GGGAGGCGG' 

s1s2後進行比較,我們有一組指標[3, 6]的代表,他們是不同的。現在,當我們添加s3考慮,這並不重要,因爲,如果s1[i]s2[i]是不同的,那麼i已經包含在集合[3, 6]我們是否有s1s2比較它,所以它不會使差是否它們中的任何一個都不同於s3[i]i將被添加到該組。另一方面,如果s1[i]s2[i]是相同的,那麼與s3[i]比較哪一個也沒有什麼不同。因此,s1s2,s3 ......的成對比較就足夠了。