Excel (VBA) |
|
(Windows 11 Pro : Excel 2019)
配列から範囲に書き込み速度を上げられますか?
投稿日時: 24/03/21 16:14:39
投稿者: blue_cars
|
---|---|
B4:J9の範囲に1つのセルに1つの数字が入ります
|
投稿日時: 24/03/21 18:08:38
投稿者: hatena
|
|
---|---|
その範囲の処理なら一瞬で終わるはずなので、
|
投稿日時: 24/03/21 18:17:55
投稿者: simple
|
|
---|---|
これは何のための作業なんでしょうか。実用性は無いようにおもいますが。
Sub test() Dim rng As Range Dim rngNum As Range Dim k As Long Dim s As String Dim total As Long Dim stotal As String Dim res As Long '合計数値の計算 Set rng = [G4].CurrentRegion '最初の行の最高位セル For k = 1 To rng.Rows.Count s = Join(Application.Index(rng.Rows.Item(k).Value, 0#), "") total = total + CLng(s) Next '書き込み先(1の位) Set rngNum = rng(rng.Count).Offset(1) '下の位から書き込む Do While total > 0 res = total Mod 10 rngNum = res Set rngNum = rngNum.Offset(0, -1) total = (total - res) / 10 Loop End Sub |
投稿日時: 24/03/21 18:19:30
投稿者: WinArrow
|
|
---|---|
VBAではなく、関数による代案を紹介します。
|
投稿日時: 24/03/21 18:36:13
投稿者: simple
|
|
---|---|
たしかに「とても遅いです」というのはどうなのかと思いました。
|
投稿日時: 24/03/21 19:05:22
投稿者: WinArrow
|
|
---|---|
私に環境では、次ンコードがエラーになりますが、
引用: 原因は Clng関数です。型違い |
投稿日時: 24/03/21 19:14:37
投稿者: Suzu
|
|
---|---|
どこが遅いのか、範囲次第とは思います。
|
投稿日時: 24/03/21 19:36:46
投稿者: blue_cars
|
|
---|---|
皆さんありがとうございます
|
投稿日時: 24/03/21 22:04:12
投稿者: WinArrow
|
|
---|---|
参考コード
Option Explicit Sub test() Dim gokei Dim Rx As Long Dim Bunkai, KETA As Long Dim myTime As Single myTime = Timer gokei = 0 With ActiveSheet For Rx = 4 To 9 gokei = gokei + WorksheetFunction.Concat(.Range(.Cells(Rx, "B"), .Cells(Rx, "J"))) Next KETA = Len(gokei) gokei = CStr(Right(String(9, "0") & gokei, 9)) ReDim Bunkai(1 To Len(gokei)) For Rx = Len(gokei) To 1 Step -1 If Rx < KETA Then Bunkai(Rx) = "" Else Bunkai(Rx) = Mid(gokei, Rx, 1) End If Next .Range("B10").Resize(1, 9).Value = Bunkai End With Debug.Print Timer - myTime End Sub 処理時間 0秒 |
投稿日時: 24/03/21 22:14:04
投稿者: hatena
|
|
---|---|
blue_cars さんの引用: 質問のコードでは、 Application.ScreenUpdating = False と画面更新を停止しているのでそうなるはずがない。 そもそも、既に指摘があるが質問のコードではエラーが出る。 実際に実行されているコードは別のものではないのだろうか。 |
投稿日時: 24/03/21 22:20:52
投稿者: WinArrow
|
|
---|---|
参考コードのみそ
|
投稿日時: 24/03/22 10:14:20
投稿者: blue_cars
|
|
---|---|
皆さんありがとうございます
|
投稿日時: 24/03/22 13:10:27
投稿者: WinArrow
|
|
---|---|
blue_cars さんの引用: 私が提示したコードの中には「MSGBOX」は無いと思いますが、 どこのステップでエラーが発生したのでしょうか? |
投稿日時: 24/03/22 13:16:02
投稿者: WinArrow
|
|
---|---|
Excelのバージョンを
|
投稿日時: 24/03/22 13:26:11
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/22 14:14:33
投稿者: sk
|
|
---|---|
引用: 引用: 引用: 例えば、アクティブシートのシートモジュールに Worksheet_Change イベントプロシージャが作成されていて、 そのワークシートのセルの値が変更されるたびに 時間のかかる何らかの処理を実行されていたりはしないでしょうか。 引用: Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Application.EnableEvents = False 引用: Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True Application.EnableEvents = True ---------------------------------------------------------- 念のため、以上のステートメントを挿入されることをお奨めします。 |
投稿日時: 24/03/22 14:29:30
投稿者: WinArrow
|
|
---|---|
Excelのバージョン情報、ありがとうございました。
Function myCONCAT(ByVal DATA) Dim i As Long myCONCAT = "" For i = LBound(DATA, 2) To UBound(DATA, 2) If DATA(1, i) <> "" Then myCONCAT = myCONCAT & DATA(1, i) End If Next End Functionを追加してみてください。 ※運用するPCが複数存在する時は、 その中で、最も低い(古い)バージョンで動くようにする必要があります。 これはVBAでもワークシート関数でも同じです。 質問時には、運用環境も説明する必要があります。 |
投稿日時: 24/03/22 14:51:39
投稿者: WinArrow
|
|
---|---|
引用: 一般的には、これを「エラーメッセージ」といいます。 |
投稿日時: 24/03/22 15:27:03
投稿者: blue_cars
|
|
---|---|
skさん
|
投稿日時: 24/03/22 15:30:30
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/22 16:42:01
投稿者: simple
|
|
---|---|
エラーになるとの報告を頂きました。
|
投稿日時: 24/03/22 16:56:00
投稿者: WinArrow
|
|
---|---|
引用: こちらでは、エラーにはなりませんが・・・・ |
投稿日時: 24/03/22 17:10:27
投稿者: WinArrow
|
|
---|---|
9行目で時間が掛かる件
|
投稿日時: 24/03/22 18:23:23
投稿者: sk
|
|
---|---|
引用: 引用: ならば、hatena さんや simple さんが言及されているように 「ループ処理において参照される実際の範囲が桁違いに 大きい(=膨大な回数のループが発生している)」か、 コードの実行を遅らせる何らかの外的要因が存在すること ぐらいしか、今のところ思いつきません。 (標準モジュール) --------------------------------------------------------------- Sub TestX001() With Application .ScreenUpdating = False .Calculation = xlCalculationManual .EnableEvents = False End With Dim rngDestination As Range Dim lngValue As Long Dim lngLength As Long Dim lngPlace As Long Dim lngColumn As Long lngValue = 6357 lngLength = Len(CStr(lngValue)) Set rngDestination = Range("B9:J9") With rngDestination .ClearContents lngColumn = .Columns.Count For lngPlace = lngLength To 1 Step -1 '検証のため、敢えてセルごとに値を代入する rngDestination.Cells(1, lngColumn).Value = Mid(CStr(lngValue), lngPlace, 1) lngColumn = lngColumn - 1 If lngColumn < 1 Then Exit For End If Next .Select End With With Application .Calculation = xlCalculationAutomatic .ScreenUpdating = True .EnableEvents = True End With Set rngDestination = Nothing End Sub --------------------------------------------------------------- 例えば、上記のように「セル範囲 B4:J8 から取得した 2 次元配列の 各要素を参照するループ処理」を省き、「セル範囲 B9:J9 の 各セルに対して値を代入する処理」を実行しただけでも かなりの実行時間を要している状態なのであれば、 それはもうアルゴリズムやコーディング手法とは 別の原因を疑うべきだと思います。 |
投稿日時: 24/03/22 19:00:53
投稿者: Suzu
|
|
---|---|
引用: あらかじめ 引用:とおことわりをしています。そんな処理は入れていませんから当然ですよ。 再計算やイベントが抑制されていたとしても 条件付き書式や、テーブルの設定がされていると、その分時間を要します。 そのあたりも確認しましょう。 新規ブックで 各セルに ご質問の内容の値を入れ、 VBA 処理 を行ってみて、同じように 2〜3秒 掛かるのかを試されてはどうでしょう。 |
投稿日時: 24/03/22 20:52:02
投稿者: WinArrow
|
|
---|---|
WinArrow さんの引用: ↑ 9行目は、結果を代入セルなんですね? >Range7"4:J9") とか ?Cells(10, s0211 + 2) = Mid(TempData0212, s0211 + 1, 1) のようなコードがあったので、9行目はデータと考えていました。 取り敢えず、このレスは無視してください。 |
投稿日時: 24/03/22 20:58:58
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/22 21:18:11
投稿者: WinArrow
|
|
---|---|
blue_cars さんの引用: 理解しました。 シートの中に、揮発性関数を使ていますか? 揮発性関数が有るとしたら、その個数(セル数)は? |
投稿日時: 24/03/22 21:32:43
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/22 22:00:22
投稿者: たらのり
|
|
---|---|
こんばんは
Sub 多分10000桁でもOK_※未検証です() ' ・ただし結果の値には前ゼロが入る ' ・限界もある(一時的に格納されるセルの値が整数で ' 表現できる範囲(15桁くらい?)でないとダメ) ' 999,999,999,999,999 / 9 = 111,111,111,111,111 行 ' くらいまではOK(ホントか?) ' ・直接セルの値を使用するので大きな桁数、行数では遅いです Dim rng As Excel.Range Dim yi As Long Dim xi As Long Dim cy As Long ' キャリー Dim ti As Long ' 合計行 Dim tm As Double tm = Timer() '' Application.ScreenUpdating = False Set rng = Sheet1.Range("B4:J8") ' 合計行を初期化 Call rng.Offset(rng.Rows.Count).Resize(1).ClearContents ti = rng.Rows.Count + 1 ' 合計行(rng の範囲の次の行) For xi = rng.Columns.Count To 1 Step -1 ' 各桁の合計処理 For yi = 1 To rng.Rows.Count rng.Cells(ti, xi).Value = rng.Cells(ti, xi).Value _ + rng.Cells(yi, xi).Value Next yi ' 桁の値の抽出・繰上げ処理 cy = rng.Cells(ti, xi).Value \ 10 If (cy <> 0) Then If (xi = 1) Then ' 最上位桁で繰上げが発生 Call MsgBox("オーバーフロー", vbCritical) Else rng.Cells(ti, xi).Value = rng.Cells(ti, xi).Value Mod 10 rng.Cells(ti, xi - 1).Value = rng.Cells(ti, xi - 1).Value + cy End If End If Next xi Set rng = Nothing '' Application.ScreenUpdating = True Debug.Print Format(Timer() - tm, "0.000") & "secs." End Sub 質問者さんから提示されたコードは読んでいなかったのですが, 数字の配置などから,勝手に上のような計算をする課題か何かかと 勘違いしてしまいました。 10,000桁,数え切れないくらいの行数の加算が可能と豪語して いますが,検証はまったくしていません。 |
投稿日時: 24/03/22 22:06:20
投稿者: blue_cars
|
|
---|---|
たらのりさん
|
投稿日時: 24/03/22 23:18:51
投稿者: hatena
|
|
---|---|
こちらで作成したサンプルシートでは、たらのりさんのや他の方のコードで問題なく実行できて、0.1秒もかかりません。
|
投稿日時: 24/03/22 23:40:26
投稿者: たらのり
|
|
---|---|
hatena さん,
|
投稿日時: 24/03/23 00:29:12
投稿者: hatena
|
|
---|---|
おもしろそうだったので、
Sub ColumnSum() Dim rng As Range Set rng = Range("J4").CurrentRegion ReDim arySum(1 To rng.Columns.Count) Dim carry As Long Dim i As Long For i = rng.Columns.Count To 1 Step -1 carry = WorksheetFunction.Sum(rng.Columns(i)) + carry arySum(i) = carry Mod 10 carry = carry \ 10 Next Set rng = rng.Offset(rng.Rows.Count).Resize(1) rng.Value = arySum i = rng.Cells(1).Column - 1 Do Until carry = 0 Or i < 1 Cells(9, i) = carry Mod 10 carry = carry \ 10 i = i - 1 Loop End Sub '速度計測用ルーチン Sub SpeedTest() Range("A9:J9").ClearContents Dim t As Single t = Timer Call ColumnSum Debug.Print Format(Timer() - t, "0.000") & "secs." End Sub 質問のデータだと、当方の環境では 0.002secs. でした。 |
投稿日時: 24/03/23 09:00:27
投稿者: abec
|
|
---|---|
ブックの中に「リンクされた図」がありませんか?
|
投稿日時: 24/03/23 09:10:07
投稿者: simple
|
|
---|---|
引用: 回答を下さい。 また、既に他の方からも指摘があったかと思いますが、 新しいブックに、今のシートの数字部分だけを再入力して(決してコピーペイストは使わない)、 事象が再現するか確認して、これも回答をいただけますか。 |
投稿日時: 24/03/23 09:55:51
投稿者: blue_cars
|
|
---|---|
みなさん
|
投稿日時: 24/03/23 11:15:46
投稿者: Suzu
|
|
---|---|
引用: では、当該ファイルをコピーし そのコピーしたファイルにおいて、そのリンク図を削除し テストしてみた場合はどうでしょう? |
投稿日時: 24/03/23 11:18:59
投稿者: WinArrow
|
|
---|---|
引用: 当たり!!!かも 個々のセルに代入するより セル範囲に一挙に代入した方が、時間が科からにと思いますので 試したみてください。 参考コード Dim Data Data = Split(Format(6357, "@,@,@,@,@,@,@,@,@"), ",") With Range("B9:J9") .NumberFormatLocal = "G/標準" .Value = Data .Value = .Value End With |
投稿日時: 24/03/23 11:26:28
投稿者: blue_cars
|
|
---|---|
Suzuさん
|
投稿日時: 24/03/23 15:41:59
投稿者: simple
|
|
---|---|
simple さんの引用: あなたのコードによる速度遅延という現象が再現するかを尋ねたのです。 |
投稿日時: 24/03/23 18:57:02
投稿者: たらのり
|
|
---|---|
こんにちは(?)
myTime1 = Timer() なので,myTime1 の値が 0 なので……(ry 処理時刻は 9:30の少し前ですかね。 hatena さん のコードのように,1行めの 1の位のセルを起点に 有効な数字が入ったセル範囲を取得すると,前ゼロの表示を 抑制したコードを書きやすいですね。 前ゼロの消去は造作もないことなので省略しましたが,その 存在自体があまり格好が良くないので…… 2つの値の加算のときのキャリーは高々 1ですが,3つ以上の 値の加算では,極端な話 何桁分もの繰り上がりが必要に なることがあり,最上位の繰り上がり処理が面倒ですね。 以下は蛇足(もうどうでもよいこと)ですが,繰り上がりの 処理は次のように cy の範囲を狭め,また除算の機会を減らし たかったです(これこそ本当の未検証です)。 ブロックスコープがある言語であれば,cy は Else 内で 宣言したいです: ' ■ 修正前 ' 桁の値の抽出・繰上げ処理 cy = rng.Cells(ti, xi).Value \ 10 ' ★ 高価な除算 If (cy <> 0) Then If (xi = 1) Then ' 最上位桁で繰上げが発生 Call MsgBox("オーバーフロー", vbCritical) Else rng.Cells(ti, xi).Value = rng.Cells(ti, xi).Value Mod 10 rng.Cells(ti, xi - 1).Value = rng.Cells(ti, xi - 1).Value + cy End If End If ' ■ 修正後 ' 桁の値の抽出・繰上げ処理 ''' cy = rng.Cells(ti, xi).Value \ 10 ' ★ 高価な除算は If (rng.Cells(ti, xi).Value > 9) Then ' 繰り上がり発生? If (xi = 1) Then ' 最上位桁で繰上げが発生 Call MsgBox("オーバーフロー", vbCritical) Else cy = rng.Cells(ti, xi).Value \ 10 ' ★ なるべく回避(ココで) rng.Cells(ti, xi).Value = rng.Cells(ti, xi).Value Mod 10 rng.Cells(ti, xi - 1).Value = rng.Cells(ti, xi - 1).Value + cy End If End If # 元々処理時間に無頓着なコードで,本当に余計なことですが |
投稿日時: 24/03/23 18:59:16
投稿者: WinArrow
|
|
---|---|
投稿日時: 24/03/23 11:18:59
Dim Data Data = Format(6357, "@,@,@,@,@,@,@,@,@") Data = Split(Replace(Data, " ", ""), ",") With Range("B9:J9") .ClearContents .NumberFormatLocal = "G/標準" .Value = Data .Value = .Value End With |
投稿日時: 24/03/23 20:57:00
投稿者: blue_cars
|
|
---|---|
たらのりさん
|
投稿日時: 24/03/23 21:22:11
投稿者: WinArrow
|
|
---|---|
引用: 空白ではありませんよ! 「@」:数字以外の部分を「1桁」のスペースに変換する指定です。 最初のコードはループして、1桁毎にセルに代入しています。 従って、1桁のスペースがセルに代入される仕様になっています。 ループ時に、スペース以外を代入する必要があったのです。 「空白」「空白文字列」「スペース」は、 意識して使い分けないと、パニックになる可能性があります。 ところで、処理時間は、どうなりましたか? |
投稿日時: 24/03/23 21:38:01
投稿者: WinArrow
|
|
---|---|
追加レス
引用: Timer関数の戻り値は、SSSS.TT・・・小数点以下は、ミリ秒です。 Long型変数に入れると、ミリ秒部分が消えてしまいます。 |
投稿日時: 24/03/23 21:47:10
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/23 21:49:40
投稿者: たらのり
|
|
---|---|
いえいえ,
|
投稿日時: 24/03/23 22:09:57
投稿者: WinArrow
|
|
---|---|
blue_cars さんの引用: 回答ありがとうございました。 なぜ、3件なんですか? できれば、前後比較して頂くと分かりやすと思います。 最初は、ループしてセルに代入していたから、9回の操作になっていましたね (数字の桁数回ではありませんよ) それが、まとめて2回の操作に変わったことで、時短になったわけです。 結果は納得できるものでしょうか? |
投稿日時: 24/03/23 22:33:09
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/24 09:04:51
投稿者: WinArrow
|
|
---|---|
だいぶ早くなったことですが、
|
投稿日時: 24/03/24 09:32:35
投稿者: WinArrow
|
|
---|---|
処理時間を訂正します。
|
投稿日時: 24/03/24 11:42:49
投稿者: blue_cars
|
|
---|---|
新規ブックで検証
|
投稿日時: 24/03/24 12:12:39
投稿者: WinArrow
|
|
---|---|
事務所のPCは、
|
投稿日時: 24/03/24 12:51:21
投稿者: blue_cars
|
|
---|---|
WinArrowさん
|
投稿日時: 24/03/24 14:36:07
投稿者: WinArrow
|
|
---|---|
引用: そうです。32Bit対応ですね。 処理速度は 32bitは、64bitに比べて、 どのくらい遅いかは、私にはわかりませんが、 明らかに遅いです。 非互換があるので、 若し、64bit環境で開発する場合は、 32bitでの動作確認が必要です。 |
投稿日時: 24/03/24 14:48:38
投稿者: blue_cars
|
|
---|---|
hatenaさん
|