注册 登录
编程论坛 VB6论坛

摆个擂

Artless 发布于 2013-04-15 19:51, 1278 次点击
看到wp版的“大家晒一晒自己的代码啊”和小z版的“两位数乘三位数等于四位数并且这九个数都不重复”
就是“123456789”9个数,取2个组成一个两位数,取3个组成一个三位数,这两个数的积就是剩下的那四位数。
例12×483=5796
找出这些组合,用VB6实现。


[ 本帖最后由 Artless 于 2013-4-15 20:13 编辑 ]
22 回复
#2
lowxiong2013-04-15 23:02
擂台胜负标准是什么?运算速度还是代码简洁程度还是两者都有?
我先来个最笨的,抛砖引玉吧
Private Sub Command1_Click()
  Dim c(9) As Integer, i As Long, j As Long, k As Long, l As Boolean, b As String
  Me.Cls
  For i = 11 To 99
    For j = 111 To 999
      If i * j > 9999 Then Exit For  '加快执行速度
      b = i & j & (i * j)
      For k = 0 To 9
        c(k) = 0
      Next
      For k = 1 To Len(b)
        c(Val(Mid(b, k, 1))) = c(Val(Mid(b, k, 1))) + 1
      Next
      l = True
      For k = 1 To 9
        If c(k) <> 1 Then l = False
      Next
      If c(0) > 0 Then l = False
      If l Then Me.Print i & "*" & j & "=" & i * j
    Next
  Next
End Sub


[ 本帖最后由 lowxiong 于 2013-4-15 23:08 编辑 ]
#3
风吹过b2013-04-16 09:17
程序代码:

Option Explicit


Private Sub Command1_Click()
Dim i As Long, j As Long, m As Long          '二个循环变量及结果
Dim o As Long                       '判断用的循环变量
Dim k(1 To 9) As Long               '数字是否已用    '49 - 57
Dim s1 As String                    '连接结果用变量
Dim k2() As Byte, b1 As Boolean     '结果的BYTE数组和测试结果变量

Cls
Print "开始时间:" & Time
Debug.Print "开始时间:" & Time

For i = 12 To 98                                    '不重复的二位数,从 12 开始到 98

    For j = 123 To 987                              '不重复的三位数,从 123 开始到 987
        m = i * j
        If m < 10000 Then                           '结果要求为 4位数,也就是小于10000
            s1 = i & j & m
            k2 = StrConv(s1, vbFromUnicode)         '转化为 BYTE 数组
            For o = 1 To 9                          '计数数组初始化
                k(o) = 0
            Next o
            
            b1 = True                               '初始为真
            For o = 0 To 8
                If k2(o) = 48 Then                          '如果含零,则直接否决
                    b1 = False
                    Exit For
                End If
                k(k2(o) - 48) = k(k2(o) - 48) + 1           '个数累计
                If k(k2(o) - 48) > 1 Then                   '发现个数超过1,否决
                    b1 = False
                    Exit For
                End If
            Next o

            If b1 Then
                Print i & " * " & j & " = " & m             '输出结果
                Debug.Print i & " * " & j & " = " & m
            End If
        Else
            Exit For
        End If
    Next j
Next i
Print "结束时间:" & Time
Debug.Print "结束时间:" & Time

End Sub



开始时间:9:09:02
12 * 483 = 5796
18 * 297 = 5346
27 * 198 = 5346
28 * 157 = 4396
39 * 186 = 7254
42 * 138 = 5796
48 * 159 = 7632
结束时间:9:09:02


代码经过了优化,没去测试是否还存在我漏掉了的情况。
#4
wp2319572013-04-16 09:21
开始时间:9:09:02
12 * 483 = 5796
18 * 297 = 5346
27 * 198 = 5346
28 * 157 = 4396
39 * 186 = 7254
42 * 138 = 5796
48 * 159 = 7632
结束时间:9:09:02



你这居然有7组答案

看来我的代码有bug啊
#5
风吹过b2013-04-16 09:32
2楼的代码,我也复制下来测试一下了,得到的也是7组答案。
2楼的代码,可以算是未优化的代码,应该可以把所有的答案都找出来。所有的数都去测试了。

但我在想:如果结果中含零,并且符合 要求时,2楼的代码会出会把这组结果也显示出来呢?     可惜假设不成立

------------------
发现有趣的东西
12 * 483 = 5796
42 * 138 = 5796

18 * 297 = 5346
27 * 198 = 5346

[ 本帖最后由 风吹过b 于 2013-4-16 09:33 编辑 ]
#6
lowxiong2013-04-16 14:17
回复 5楼 风吹过b
If c(0) > 0 Then l = False,这句就是去0的,的确有一组中间为0的数据,不过大于9999,你的比我的总共少循环48次,再就是strconv执行效率不知道会比mid函数高多少,可用timegettime到毫秒级计算下具体执行时间就知道了。


[ 本帖最后由 lowxiong 于 2013-4-16 14:22 编辑 ]
#7
Artless2013-04-16 14:48
2#耗时:343毫秒
3#耗时:110毫秒
#8
lowxiong2013-04-16 15:05
回复 7楼 Artless
我也测试了下,我的283毫秒,风兄107毫秒。楼主机器要换了,我08年的老本本比你的跑的都快。看来strconv函数执行效率高好多的。
#9
风吹过b2013-04-16 15:49
1、循环次数,我比 lowxiong 不此少 48 次。
   49毫秒, 11090 次。
   147毫秒,12784 次。
   在内循环第一第语句前计次。
   可惜 这个电脑不是自己的。

2、strconv 函数,不一定就执行效率高。
   关键在于,strconv 函数只需要调用一次,而你的需要调用 9次 mid 和 val 函数。 这个方面 不平等。


我的代码这段继续优化一下:
程序代码:

            For o = 0 To 8
                k2(o) = k2(o) - 48                  'val
                If k2(o) = 0 Then                          '如果含零,则直接否决
                    b1 = False
                    Exit For
                End If
                k(k2(o)) = k(k2(o)) + 1             '个数累计
                If k(k2(o)) > 1 Then                    '发现个数超过1,否决
                    b1 = False
                    Exit For
                End If
            Next o

优化的方向,减少无效计算过程,加一条命令直接生成结果数,精减原调用方式里4次引用时临时计算。由4次计算变1次计算,没有增加内存开销。

另一种优化
程序代码:

            For o = 1 To 9                          '计数数组初始化
                k(o) = False
            Next o
            
            b1 = True                               '初始为真
            For o = 0 To 8
                k2(o) = k2(o) - 48                  'val
                If k2(o) = 0 Then                          '如果含零,则直接否决
                    b1 = False
                    Exit For
                End If
                If k(k2(o)) Then                     '发现个数已用过,否决
                    b1 = False
                    Exit For
                Else
                    k(k2(o)) = True             '标记为用过
                End If
            Next o

优化方向,使用 逻辑型 数据变量,减少 比较运算符
-------------------
比较奇怪,一般来说 使用 boolean 应该比 比较符号要快,但从这二段代码运行结果来看,
基本没任何差异,也许循环次数太少的原因吧。
#10
lowxiong2013-04-16 18:18
风兄分析的正确,我的大循环应该比风兄多循环99*999-86*864=24597次,但大循环次数不是影响速度的关键,关键是我的mid函数循环次数和我多了一个9次循环判断。改成下述代码后只需要86毫秒
Private Sub Command1_Click()
  Dim c(9) As Integer, i As Long, j As Long, k As Long, l As Boolean, b() As Byte, e As Integer
  Me.Cls
  t = timeGetTime
  For i = 11 To 99
    For j = 111 To 999
      If i * j > 9999 Then Exit For  '大于四位数的不用做,没有这一句就真多循环24597次以上
      b = i & j & i * j              '直接转换成byte数组
      For k = 0 To 9
        c(k) = 0                     '清空用于判断123456789使用一次的数组,未使用strconv,会是utf8,多用一倍内存
      Next
      l = True
      For k = 0 To UBound(b) Step 2
        e = b(k) - 48
        If e = 0 Or c(e) > 0 Then
          '这里是使用风兄的算法,一次判断完成,我的只是对标志+1,后再用一个9次循环判断
          l = False
          Exit For
        End If
        c(e) = c(e) + 1
      Next
      If l Then Me.Print i & "*" & j & "=" & i * j
    Next
  Next
  MsgBox timeGetTime - t    '显示耗时,此算法平均耗时86毫秒,少用20毫秒,但我的大循环仍比风兄的多,按风兄12-98,123-987,则需要75毫秒
End Sub

#11
风吹过b2013-04-17 08:41
今天再测试了一下,在我的电脑上 lowxiong 的代码比我的代码快 7 毫秒左右。
测试方法:
去掉 显示部分,然后其它不变,循环调用 100次,然后看最后的计时。

程序代码:

Dim t1 As Long, t2 As Long
Dim t3 As Long, t4 As Long
Dim i As Long

t1 = timeGetTime
For i = 1 To 100
Cls
Call Command1_Click
Next i
t2 = timeGetTime
For i = 1 To 100
DoEvents
Next i

t3 = timeGetTime
For i = 1 To 100
Cls
Call Command2_Click
Next i
t4 = timeGetTime

For i = 1 To 100
DoEvents
Next i

Cls
Print "1"; t1; t2, t2 - t1
Print "2"; t3; t4, t4 - t3

#12
zhengang10262013-04-17 16:00
就我等新手看,lowxion版主最初的代码最好理解易懂。真佩服几位版主对VB的熟练掌握,同时也对他们精益求精的精神表示敬佩!
#13
zklhp2013-04-17 16:04
看到wp版的“大家晒一晒自己的代码啊”和小z版的“两位数乘三位数等于四位数并且这九个数都不重复”
就是“123456789”9个数,取2个组成一个两位数,取3个组成一个三位数,这两个数的积就是剩下的那四位数。

貌似着俩不等价啊 怪不得你们的答案组数少啊 没有0。。。。
#14
zklhp2013-04-17 16:05
顺便膜拜各位的VB水平 虽然我不大喜欢VB。。。
#15
zhengang10262013-04-18 08:59
请问怎么才能测到毫秒级?我分别在程序开始和结束位置用了t = timeGetTime、 MsgBox timeGetTime - t ,可还是显示0秒啊,难道是我的电脑太快?
#16
风吹过b2013-04-18 10:02
你申明了 timeGetTime 这个函数吗???

如果你没申明的话,也不想申明的话,那么你就用
timer 这个函数吧。
timer 函数,返回的是 秒,但会带二位小数,也就是精度是 10毫秒。
timeGetTime API函数,返回的是 毫秒,不带小数,精度是 1毫秒。

t=timer
msgbox timer-t

结果带小数,乘以1000,就是 毫秒了,但精度只有 10毫秒。

[ 本帖最后由 风吹过b 于 2013-4-18 10:05 编辑 ]
#17
zhengang10262013-04-18 12:33
哦,我还当timeGetTime 是VB内部函数呢,谢谢指教。
#18
xbj_hyml2013-04-18 12:36
学习中...
    顺便飘过...
#19
好聚好散2013-04-18 12:38
高手
#20
ease10002013-05-14 18:13
看了前面几位的代码都感觉有点复杂,可读性不强
为什么不用简单点的呢:
Private Sub Command1_Click()
    For i = 12 To 98
        For j = 123 To 987
            If i * j < 10000 Then
                n = i & j & i * j
                For k = 1 To 9
                    If InStr(n, k) = 0 Then Exit For
                Next k
                If k > 9 Then Print i & "*" & j & "=" & i * j
            End If
        Next j
    Next i
End Sub
#21
ease10002013-05-14 18:21
为什么我不能发帖?发了2次都无效,违规了吗?请大家帮我看看

用WebBrowser登录QQ的问题
'源码如下
Private Sub Command1_Click()
    WebBrowser1.Navigate2 "http://ui.ptlogin2.
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, Url As Variant)
    On Error Resume Next
    If InStr(Url, "ui.ptlogin2.) <> 0 Then '登录页面
        pDisp.Document.getElementById("u").Value = "392354601"      '账号

        pDisp.Document.getElementById("p").Value = "a123456"        '密码
        pDisp.Document.getElementById("p").Click                    '密码框需要手动点击一下,这句没有效果?

        'pDisp.Document.getElementById("verifycode").Value = "xmsz" '验证码

        'pDisp.Document.getElementById("login_button").Click         '登录
    End If
End Sub

直接按登录键不能正确登录 , 会出现验证码的提示框
如果手动点击密码框一下 , 再按登录键则可正常登录
求高手指点
#22
好聚好散2013-05-17 20:19
。。。。
#23
zhengang10262014-06-06 20:22
以下是引用ease1000在2013-5-14 18:13:57的发言:

看了前面几位的代码都感觉有点复杂,可读性不强
为什么不用简单点的呢:
Private Sub Command1_Click()
    For i = 12 To 98
        For j = 123 To 987
            If i * j < 10000 Then
                n = i & j & i * j
                For k = 1 To 9
                    If InStr(n, k) = 0 Then Exit For
                Next k
                If k > 9 Then Print i & "*" & j & "=" & i * j
            End If
        Next j
    Next i
End Sub

真是独辟蹊径!非常好的算法,既易懂且运算速度又快(我测试了下,比以上几个版主的代码都要快!)
1