一个算法问题

有网友在我《在Excel中使用VBA来筛选数据》(http://www.cnblogs.com/maweifeng/archive/2005/06/13/71504.html)一文中提出了如下问题,大概考虑了一下,解答如下,欢迎指正。

问题描述:

有一组数据,数据量很大(大于10000,假设为N),对这N组数据(每组有M个数值且已排好序),如果N组中某几组数据中有h个数字相同(例如7),就认为这几组数据都是相似的,然后把它们一起存放在一个指定的位置。

例如以下数据:
 
(行号)       (数据)
0001    2 4 6 10 12 14 16 18 20 22 24 26
0002    4 10 12 16 20 22 26
…….
0014    2 4 12 14 18 22 24

就认为0001,0002,0014行数据是相似的,把它们存放在指定的位置。
 
0003    1 5 7 12 14 22 28 39 50 55 60
0007    5 7 14 22 39 55 60
0013    1 7 12 14 28 39 55
…...
0100    5 7 12 22 28 39 50 60
     
这几组数据也是类似的,把它们也存在上一组类似数据的后面。 其他的以此类推。

实现算法:

for i = 1 to N
    for j = i + 1 to N
        m , n = 0
        count = 0
        比较 i 行和 j 行
        While         
            if A[i][m] < A[j][n]
                m++
            else if A[i][m] == A[j][n]
                count++
                m++, n++
            else
                n++

            if count > 7
                break
                标记 i 行和 j 行相似,做相应操作

大概算法很明了,无需过多说明,执行效率为 N2*M (1/2 * N2 * M)。自己再想不出什么更好的算法。有一个问题是:如果第1,3,4,5行类似,在i为3时,4,5行也会伴随扫描进来为类似,但实际上已经不需要,而3有另一组数据和7,8行类似,因此,需要标记这类现象。

如果已经标记的行再不需要参与计算,则将标记的行在循环时跳过即可,效率大概为:N(N-2)*M/2。

对于这个问题,因为各行需要两两比较,因此至少行之间的比较需要N*(N-1)/2次运算,但如果行之间比较的结果在其他行比较时可以用的上的话,应该还有优化的余地。

附带VBA代码:


 1Public Sub SelectSimilar()
 2    
 3    Dim N As Long, M As Long    '行数和列数
 4    Dim i As Long, j As Long
 5    Dim pIndex1 As Long, pIndex2 As Long, count As Long
 6    Dim value1 As Long
 7    Dim value2 As Long
 8    Dim pIndex3 As Long
 9    Dim k As Long, l As Long
10    
11    pIndex3 = 1
12    
13    N = 21
14    M = Asc("A"+ 12
15    
16    '外部循环
17    For i = 1 To N Step 1
18        
19        '拷贝第一行
20        Sheets("Sheet1").Select
21        Sheets("Sheet1").Rows(CStr(i) & ":" & CStr(i)).Select
22        Selection.Copy
23        Sheets("Sheet2").Select
24        Sheets("Sheet2").Rows(CStr(pIndex3) & ":" & CStr(pIndex3)).Select
25        ActiveSheet.Paste
26        pIndex3 = pIndex3 + 1
27        
28        '获取标志值
29        k = Sheets("sheet1").Range(Chr(M + 2& CStr(i)).Value
30        
31        For j = i + 1 To N Step 1
32            pIndex1 = Asc("A")
33            pIndex2 = Asc("B")
34            count = 1
35            
36            '获取标志值
37            l = Sheets("sheet1").Range(Chr(M + 2& CStr(j)).Value
38            
39            Do While pIndex1 <= M And pIndex2 <= M
40                
41                '如果2行都和前边的行相似,则不再比较
42                If k = l And l > i Then
43                    Exit Do
44                End If
45            
46                value1 = Sheets("Sheet1").Range(Chr(pIndex1) & CStr(i)).Value
47                value2 = Sheets("Sheet1").Range(Chr(pIndex2) & CStr(j)).Value
48                
49                '比较大小,改变当前值位置
50                If value1 < value2 Then
51                    pIndex1 = pIndex1 + 1
52                ElseIf value1 = value2 Then
53                    pIndex1 = pIndex1 + 1
54                    pIndex2 = pIndex2 + 1
55                    count = count + 1
56                Else
57                    pIndex2 = pIndex2 + 1
58                End If
59                
60                '如果符合条件,则做相应操作
61                If count >= 7 Then
62                    '标记此行和i行相似
63                    Sheets("sheet1").Range(Chr(M + 2& CStr(j)).Value = i
64                    
65                    '将数据拷贝到sheet2
66                    Sheets("Sheet1").Select
67                    Sheets("Sheet1").Rows(CStr(j) & ":" & CStr(j)).Select
68                    Selection.Copy
69                    Sheets("Sheet2").Select
70                    Sheets("Sheet2").Rows(CStr(pIndex3) & ":" & CStr(pIndex3)).Select
71                    ActiveSheet.Paste
72                    pIndex3 = pIndex3 + 1
73                    Exit Do
74                End If
75            Loop
76        Next j
77        
78        pIndex3 = pIndex3 + 1
79        
80    Next i
81        
82End Sub



posted on 2005-06-13 18:22  马维峰  阅读(1386)  评论(4编辑  收藏  举报