Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 11 Home : Excel 2021)
フィボナッチ数列
投稿日時: 23/11/10 20:45:17
投稿者: TomVla

これは正方形の内部に「縦横に」「平行に」線分がいくつか引かれる図が得られるはずですが、
斜線が不規則に表れてしまい正しい図が得られません。
このプログラムに不具合があるのでしょうか。ご指摘おねがいします。
(尚これはフィボナッチ数列を図形化するものです)
 
Sub Divide()
    Dim ws As Worksheet
    Dim shp As Shape
    Dim P As Single, R As Single
    Dim Q As Single, S As Single
    Dim T As Single, N As Single
 
    P = SQR(5)
    Q = (P + 1) / 2
    R = (-P + 1) / 2
    S = 1 / P
     
    ' 描画するワークシートを選択(必要に応じて変更)
    Set ws = ThisWorkbook.Sheets("Sheet3")
 
    FOR N = 1 TO 12
 
    T = S * (Q^N - R^N)
 
    ' 直線を描画
    Set shp = ws.Shapes.AddLine(T + 119, 0, 0, 399)
    Set shp = ws.Shapes.AddLine(521 - T, 0, 0, 399)
    Set shp = ws.Shapes.AddLine(120, T - 1, 400, 0)
    Set shp = ws.Shapes.AddLine(120, 400 - T, 400, 0)
 
    Next
     
    ' 直線のスタイルや色などを設定(必要に応じて変更)
    With shp
        .Line.Weight = 2 ' 線の太さ
        .Line.ForeColor.RGB = RGB(0, 0, 255) ' 線の色(青色)
    End With
End Sub
 

回答
投稿日時: 23/11/11 06:22:47
投稿者: simple

 フィボナッチ数列は作成できていると思います。
 
 直線描画は命じたとおりに動作していると思いますが、
 あなたが思う「正しい図」の説明がなく何を描画したいかが不明なので、それ以上は回答できません。
 (思い当たることはありますが、テレパシー使いではないので控えます。
   少なくともExcelVBAの質問からは、はずれる話かと思います。)
 
 また、最後にshapeにひとつだけ書式設定している意味もよくわかりません。
 これは最後に作成したshapeだけが対象になると思います。

投稿日時: 23/11/11 07:07:34
投稿者: TomVla

simpleさん
早速ご指摘ありがとうございます。
 
>あなたが思う「正しい図」の説明がなく何を描画したいかが不明なので、
正しい図とは斜線の集合でなく、升目上の正方形、長方形の集合と思っています。
そこには少なくとも「斜線」は存在しません。
 
従って
> 直線描画は命じたとおりに動作していると思いますが、
ということが理解できません。

回答
投稿日時: 23/11/11 07:26:12
投稿者: simple

引用:
> 直線描画は命じたとおりに動作していると思いますが、
ということが理解できません。
うーむ。
Set shp = ws.Shapes.AddLine(T + 119, 0, 0, 399)
は、どことどこを結ぶ線と思っていますか?

回答
投稿日時: 23/11/11 12:18:16
投稿者: simple

よく確認してください。
「Shapes.AddLine メソッド」
https://learn.microsoft.com/ja-jp/office/vba/api/excel.shapes.addline
私はこれで。

回答
投稿日時: 23/11/11 12:54:19
投稿者: Suzu

simple さんの引用:

Set shp = ws.Shapes.AddLine(T + 119, 0, 0, 399)
は、どことどこを結ぶ線と思っていますか?

 
Shapes.AddLine メソッド (Excel)
https://learn.microsoft.com/ja-jp/office/vba/api/excel.shapes.addline?f1url=%3FappId%3DDev11IDEF1%26l%3Dja-JP%26k%3Dk(vbaxl10.chm638081)%3Bk(TargetFrameworkMoniker-Office.Version%3Dv16)%26rd%3Dtrue
 
引用:
式。AddLine (BeginX、 BeginY、 EndX、 EndY)

 
つまり、
AddLine の それぞれの引数は
 第一引数 : 始点 の X座標
 第二引数 : 始点 の Y座標
 第三引数 : 終点 の X座標
 第四引数 : 終点 の Y座標
を指定します。
 
変数 T が 「1」の時の それぞれの引数は
 第一引数 : 始点 の X座標 521-1 = 520
 第二引数 : 始点 の Y座標      0 → 始点: C1 セルの 左上
  
 第三引数 : 終点 の X座標      0
 第四引数 : 終点 の Y座標     399  → 終点: A22 セル の左
 
になりますから、斜線になって当然です。
 
 
コード に 赤字を追加すれば 各変数の挙動がワークシート上に表示されます。
F列に T の値が表示されますから、フィボナッチ数列が得られている事は確認できます。
 
  ' 描画するワークシートを選択(必要に応じて変更)
  Set ws = ThisWorkbook.Sheets("Sheet3")

[color=red]  ws.Cells(1, 1) = "N"
  ws.Cells(1, 2) = "P"
  ws.Cells(1, 3) = "Q"
  ws.Cells(1, 4) = "R"
  ws.Cells(1, 5) = "S"
  ws.Cells(1, 6) = "T"
[/color]  For N = 1 To 12
    T = S * (Q ^ N - R ^ N)
[color=red]    ws.Cells(N + 1, 1) = N
    ws.Cells(N + 1, 2) = P
    ws.Cells(N + 1, 3) = Q
    ws.Cells(N + 1, 4) = R
    ws.Cells(N + 1, 5) = S
    ws.Cells(N + 1, 6) = T
[/color]    ' 直線を描画
    Set shp = ws.Shapes.AddLine(T + 119, 0, 0, 399)
'    ws.Shapes.AddShape Type:=msoShapeRectangle, Left:=0, Top:=0, Width:=T * 100, Height:=T * 100
'    Stop
    Set shp = ws.Shapes.AddLine(521 - T, 0, 0, 399)

 
 
そのフィボナッチ数列を、Shapeとしてどう見せるか?
それは 仕様の話であり、回答者が決めつける話ではないので、
きちんと、質問者さんが提示する必要があります。
 
それが、simpleさんが仰っている
引用:
あなたが思う「正しい図」の説明がなく何を描画したいかが不明なので、それ以上は回答できません。

の意図です。
 
 
 
 
フィボナッチ数列と言う事なので、在りうるとすれば
 
Wikipedia
フィボナッチ数
https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0
の様な 正方形 の集合
 
または、それらのある一つ角を結んだ螺旋曲線
 
のどちらであると予測できます。
 
直線を使っていますから前者なのでしょう。
当方なら、 AddShape Type:=msoShapeRectangle の正方形で描くでしょうかね。
 
だとしても、始点終点の XY座標は
N の T のみでなく
N-1 の ときの T の X座標・Y座標にも影響を受ける事になり
 
    Set shp = ws.Shapes.AddLine(T + 119, 0, 0, 399)
    Set shp = ws.Shapes.AddLine(521 - T, 0, 0, 399)
    Set shp = ws.Shapes.AddLine(120, T - 1, 400, 0)
    Set shp = ws.Shapes.AddLine(120, 400 - T, 400, 0)
 
の様な 0、339、400、120 の 様な固定値では無いはずですね。
 
まぁ、先に提示したコードの様に、セルに それぞれの計算結果値を表示させる様にし
それぞれの 数値が 意図した値になっているのか、確認する様にしましょう。

投稿日時: 23/11/12 07:17:23
投稿者: TomVla

Suzuさん  simpleさん
丁寧なご助言ありがとうございます。
実はこのプログラムは昔扱ったN-BASIC(NはNEC)のプログラムをportingしたものです。
別のプログラムでうまくいったのでこのプログラムでもやってみましたが、少し勝手が違うようです。
N-BASICでは、このような簡単なプログラムできれいな格子模様が現れました。
頂いたアドバイスで再度検討します。時間がかかりそうですが、うまくいったら別途報告します。
とりあえずお礼まで。

回答
投稿日時: 23/11/12 08:36:01
投稿者: simple

[Rangeの指定方法]というスレッドで
> https://www.moug.net/faq/viewtopic.php?t=82422
> に関係すると思われる記事を書きました。お知らせします。
と書きましたが、まったく反応なしでした。
 
今回も余り期待できませんが、二つの方法(正方形の接続方法)について、
参考コードを書いてしまったので、興ざめかもしれませんが、参考にしてください。
 

Option Explicit

Dim fibo() As Long

Sub test1() '単純に右、下に正方形を追加していく場合
    Const m   As Long = 10
    Dim k As Long
    
    ReDim fibo(0 To m)
    Call setFibo(m)     'フィボナッチ数の作成
    
    Call 図形消去       ' Autoshapeを初期化
    
    ReDim xpos!(m), ypos!(m), n&(m)     '各shapeの座標と大きさ
    
    xpos(0) = 2!: ypos(0) = 2!: n(0) = fibo(0) * 10
    Call rect(xpos(0), ypos(0), n(0))   '正方形の描画

    For k = 1 To m
        If k Mod 2 = 1 Then
            xpos(k) = xpos(k - 1) + n(k - 1)
            ypos(k) = ypos(0)
        Else
            xpos(k) = xpos(k - 2)
            ypos(k) = ypos(k - 2) + n(k - 2)
        End If
        n(k) = fibo(k) * 10
        Call rect(xpos(k), ypos(k), n(k))
    Next
End Sub

Sub test2()     ' らせん形の位置に正方形を追加していく場合
    Const m   As Long = 10
    
    ReDim fibo(0 To m)
    Call setFibo(m)
    
    ' Autoshapeをいったん消去
    Call 図形消去
    
    ReDim xpos!(m), ypos!(m), n&(m)
    Dim k As Long
    
    xpos(0) = 500!: ypos(0) = 475!    ' 最初のAutoShapeの位置
    n(0) = fibo(0) * 10
    Call rect(xpos(0), ypos(0), n(0)) '正方形の描画

    For k = 1 To m
        Select Case k Mod 4
        Case 1
            xpos(k) = xpos(k - 1) + n(k - 1)
            ypos(k) = ypos(k - 1)
        Case 2
            xpos(k) = xpos(k - 2)
            ypos(k) = ypos(k - 1) + n(k - 1)
        Case 3
            xpos(k) = xpos(k - 1) - fibo(k) * 10
            ypos(k) = ypos(k - 2)
        Case 0
            xpos(k) = xpos(k - 1)
            ypos(k) = ypos(k - 1) - fibo(k) * 10
        End Select
        
        n(k) = fibo(k) * 10
        Call rect(xpos(k), ypos(k), n(k)) '正方形の描画
    Next
End Sub

Function rect(x!, y!, n&)
    With ActiveSheet.Shapes.AddShape(msoShapeRectangle, x, y, n, n)
        .Fill.Visible = msoFalse
        With .Line
            .Visible = msoTrue
            .Weight = 1.5
        End With
    End With
End Function

Function setFibo(k As Long)
    Dim j&
    fibo(0) = 1: fibo(1) = 1
    For j = 2 To k
        fibo(j) = fibo(j - 1) + fibo(j - 2)
    Next
End Function

Sub 図形消去()
    Dim shp As Shape
    For Each shp In ActiveSheet.Shapes
        If shp.Type = msoAutoShape Then
            shp.Delete
        End If
    Next
End Sub

投稿日時: 23/11/12 09:43:27
投稿者: TomVla

(正方形の接続方法)のところで、実行すると
> Call setFibo(m) 'フィボナッチ数の作成
のラインで
 
コンパイルエラー SubまたはFunctionが定義されていません
 
というメッセージが出て進めません。
恐縮ですが対応方法をご教示ください。

回答
投稿日時: 23/11/12 10:28:34
投稿者: simple

改めて、投稿内容を新規ブックにコピーペイストして実行しましたが、そういう状態にはなりません。
もう一度確認してください。

回答
投稿日時: 23/11/12 12:10:36
投稿者: simple

冒頭にある
Dim fibo() As Long
をコピーし忘れていると思います。
飾りではなく、それにはきちんとした意味があるのです。

投稿日時: 23/11/12 13:47:56
投稿者: TomVla

やはり動作しません。
 
Option Explicit
Dim fibo() As Long
を含め
Sub test1() '単純に右、下に正方形を追加していく場合
のブロックのみコピーしてrunすると
コンパイルエラー SubまたはFunctionが定義されていません
のエラーメッセージが出ます。
 
Option Explicit
Dim fibo() As Long
を含め、全体をコピーしてrunすると
エラーメッセージは出ませんが何も現れません。
 
 

回答
投稿日時: 23/11/12 16:24:55
投稿者: simple

(1)

引用:
Sub test1() '単純に右、下に正方形を追加していく場合
のブロックのみコピーしてrunすると
コンパイルエラー SubまたはFunctionが定義されていません
のエラーメッセージが出ます。
test1のなかで、Call ×× としているところで、××のSubプロシージャや
Functionプロシージャを呼び出しています。それらのプロシージャが無ければ
エラーになるのは至極当然のことです。基本的な理解をしっかりされることを推奨します。
(2)
1. 標準モジュールに貼り付けることを一応前提にしています。
2.図形は、アクティブシートに描画されます。
 
全体をコピー貼り付けして、test1をステップ実行してみて下さい。
特に、描画する命令を実行したときに、描画がされるか確認してください。

投稿日時: 23/11/13 07:25:08
投稿者: TomVla

ありがとうございます。
>フィボナッチ数
>https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0
にあるような図形を描画することができました。
 
ただ、残念ながらN-BASICでの描画図形とは異なります。
N-BASICでは「絶対座標」と「相対座標」を組み合わせたような指定が必要です。
VBAではそのような指定はできないですね。

回答
投稿日時: 23/11/13 09:38:56
投稿者: simple

どのあたりに勘違いがあったんですか? 無理に説明を求めるものではありませんが。
大騒ぎするんだからよほどのことだったんですか?
 
> ただ、残念ながらN-BASICでの描画図形とは異なります。
別に残念でもないです。時計回り、反時計回りの違いですか?
道具の違いじゃなく、それを使った実行命令が違うだけじゃないですか?
たいていのことは同じようにできると思いますけど。
 
> N-BASICでは「絶対座標」と「相対座標」を組み合わせたような指定が必要です。
> VBAではそのような指定はできないですね。

https://learn.microsoft.com/ja-jp/office/vba/api/excel.shapes.addshape
を研究されるとよいでしょう。(既に使っていますが)
左上の位置を指定し、横と縦の幅を指定するものですが、同じものじゃないですか?
 
いっそのこと、あなたの好きな N-BASICのコードを提示されたらいかがですか?
皆さんからコメントが頂けるかもしれません。

投稿日時: 23/11/13 19:27:16
投稿者: TomVla

ご参考:
N-BASICのプログラムです。10行のシンプルなものです。
 
110 SCREEN 2:WIDTH 80,25:CONSOLE 0,25,0
120 P=SQR(5):Q=(P+1)/2:R=(-P+1)/2:S=1/P
130 FOR N=1 TO 12
140 T=S*(Q^N-R^N)
150 LINE(T+119,0)-STEP(0,399)
160 LINE(521-T,0)-STEP(0,399)
170 LINE(120,T-1)-STEP(400,0)
180 LINE(120,400-T)-STEP(400,0)
190 NEXT
200 END
 
110行は書式等を設定するもの
120行〜190行を今回VBAにportingしようとしています。
160行にて
LINE(X1,Y1)は、描く線/矩形の開始点を表す絶対座標
STEP(X1,Y1)は、描く線/矩形の終了点を表す相対座標
X1,Y1は、X軸方向、Y軸方向の相対座標を指定する数/数式
以上

回答
投稿日時: 23/11/13 21:03:38
投稿者: simple

既に回答したとおりですが、念のため。

Sub test3()
    Dim ws    As Worksheet
    Dim P As Single, R As Single
    Dim Q As Single, S As Single
    Dim T As Single, n As Single

    P = Sqr(5)
    Q = (P + 1) / 2
    R = (-P + 1) / 2
    S = 1 / P

    ' 描画するワークシートを選択(必要に応じて変更)
    Set ws = ThisWorkbook.Sheets("Sheet3")

    For n = 1 To 12
        T = S * (Q ^ n - R ^ n)
        ' 直線を描画
        Call myLine(T + 119, 0, 0, 399)
        Call myLine(521 - T, 0, 0, 399)
        Call myLine(120, T - 1, 400, 0)
        Call myLine(120, 400 - T, 400, 0)
    Next
End Sub

Function myLine(x!, y!, m!, n!)
    With ActiveSheet.Shapes.AddShape(msoShapeRectangle, x, y, m, n)
        .Fill.Visible = msoFalse
        With .Line
            .Visible = msoTrue
            .ForeColor.RGB = RGB(0, 0, 255)
            .Weight = 2#
        End With
    End With
End Function

投稿日時: 23/11/13 21:44:25
投稿者: TomVla

 simpleさん
ありがとうございます。ご指摘のように
>エラーになるのは至極当然のことです。基本的な理解をしっかりされることを推奨します。
が根本原因でした。今後しっかり身に着けていきます。
Frasctalについても、引き続き検討します。