エクセル雑感
数値変数の値を別の変数を使わずに入れ替える

ExcelマクロVBAとエクセル関数についての私的雑感
最終更新日:2020-10-14

数値変数の値を別の変数を使わずに入れ替える


ツイッターで出したエクセルVBAのお題です。


数値が入っている3つの変数を、他の変数を使わずに値を入れ替えるという問題です。


問題を出したツイート

【VBA問題】
変数a,b,cに整数値が入っています。
これをa>b>cとなるように値を入れ替えてください。
ただし、変数(プロパティ含む)の使用は不可、変数を使用せずに値を入れ替えてください。
(関数戻り値の無名は使ってよい)
なお、a,b,cのデータ型は随意。


VBA マクロ 数値変数の値を別の変数を使わずに入れ替える
https://twitter.com/yamaoka_ss/status/1315298046880620544

例えばこういう事です。
Sub test()
 Dim a, b, c
 a = 5
 b = 9
 c = 7
 Call fnc(a, b, c)
 Debug.Print a, b, c '→9,7,5
End Sub
Function fnc(a, b, c)
 'ここで変数を宣言せずに値を入れ替える
End Function

VBA マクロ 数値変数の値を別の変数を使わずに入れ替える


後から追加したツイートで、余計に紛れを生じさせてしまった気がします。
ツイートした後に、あー、と思ったのですが、、、
そういう事に気づくかどうも含めて、ツイートならではのお題としては良いのかもしれないと思い、そのままにしました。


お寄せいただいた回答

以下はツイッターの検索で、元のツイートが引用されているツイートを検索したものです。
一つずつ紹介したいのですが数が多くて紹介しきれなくなってしまいました。
頂いた回答は、こちらの検索結果からご覧ください。

https://twitter.com/search?lang=ja&q=https%3A%2F%2Ftwitter.com%2Fyamaoka_ss%2Fstatus%2F1315298046880620544&src=typed_query

大別すると

・2値置換の定跡を使う
・配列に入れてシート関数を使う
・独創的な方法(再帰含む) :上記2通りの応用です。

以下の私の用意した解答は、2値置換の定跡と配列を利用したものになります。


用意した解答

まず最初に、
例として関数fncと書いてしまったので、このfncを使えば簡単に入れ替えできてしまいます。
fncに値を入れられるので、1つ余分に変数があるようなものなので簡単に入れ替えられます。
それはそれで、そこに気づいたということで回答として良いと思います。
さすがに、以下はこの方法を除いた解答になります。

先に断っておきますが、数学的な説明として正確な説明になっていない部分があってもご容赦ください。

2値に対してある演算を行って得られる値があるとき、
例えば、
f(x,y) = z
このとき、zとy(またはx)を使って逆算できる場合、
f'(z,y) = x
f'(z,x) = y
これが一意の値として求められるような演算fとf'があるなら、zがあればxまたはyの片方は無くても良いという事になりす。
簡単に例示します。

5 + 7 = 12
このとき、7と12があればいつでも5は求めることができます。
12 - 7 = 5
a,bで書くと、
a + b = z
このとき、bとzがあればaはいつでも求められます。
そこで、VBAで、
a = a + b ・・・ ①
この時点で、(a + b)とbの値が手元にあります。
これでいつでもaは求めることができます。
b = a - b ・・・ ②
aの値は元の(a + b)なので、上の式の結果は元のaになります。
a = a - b ・・・ ③
aの値は元の(a + b)、bの値は元のaの値なので、この式の結果は元のbの値になります。
①②③の結果、a,bの値は入れ替わります。
これを使ってVBAでa,b,cを入れ替えます。

Function fnc(a, b, c)
  If a < b Then
    a = a + b
    b = a - b
    a = a - b
  End If
  If a < c Then
    a = a + c
    c = a - c
    a = a - c
  End If
  If b < c Then
    b = b + c
    c = b - c
    b = b - c
  End If
End Function

a,b,cの3値なので、3通りの比較が必要になります。
ここでは+と-でやりましたが正確に逆算できるならどんな演算子でも構わないという事になります。
といってもVBAに用意されている演算子は限られていますが。
※一意に逆算できるのなら、独自の関数でやっても構いません。

2値の入れ替えの方法として、プログラミングで有名?なのはXor(排他的論理和)になります。
詳細まで説明しきれませんが、概要だけ。
Xorはビット演算で、どちらか片方だけが1の時のみ1になります。

5 Xor 7 = 2
0101 Xor 0111 = 0010
5 0 1 0 1
7 0 1 1 1
2 0 0 1 0

2 Xor 7 = 5
0010 Xor 0111 = 0101
2 0 0 1 0
7 0 1 1 1
5 0 1 0 1

2 Xor 5 = 7
0010 Xor 0101 = 0111
2 0 0 1 0
5 0 1 0 1
7 0 1 1 1

このように2値のXorの結果と元の片方の値のXorをとると、もう片方の値を求めることができます。
つまり逆算ができるという事です。
これを使ってVBAでa,b,cを入れ替えます。

Function fnc(a, b, c)
  If a < b Then
    a = a Xor b
    b = a Xor b
    a = a Xor b
  End If
  If a < c Then
    a = a Xor c
    c = a Xor c
    a = a Xor c
  End If
  If b < c Then
    b = b Xor c
    c = b Xor c
    b = b Xor c
  End If
End Function


ここまでは、エクセル関係ないですし、VBAである必要がないものです。
それではつまらないですよね(笑)
以下は、エクセルVBAならではのやり方になります。

やることは3値を配列にしてどれかの変数に入れてしまいます。
3値が配列に入っているので、
後は大きい順(もしくは小さい順)に取り出せば良いことになります。
まずは、1つずつ比較して取り出してみます。

Function fnc3(a, b, c)
  a = Array(a, b, c)
  Select Case True
    'a(0)<a(1),a(2)
    Case a(0) < a(1) And a(0) < a(2)
      If a(1) < a(2) Then
        c = a(0): b = a(1): a = a(2)
      Else
        c = a(0): b = a(2): a = a(1)
      End If
    'a(1)<a(0),a(0)
    Case a(1) < a(0) And a(1) < a(2)
      If a(0) < a(2) Then
        c = a(1): b = a(0): a = a(2)
      Else
        c = a(1): b = a(2): a = a(0)
      End If
    'a(2)<a(0),a(1)
    Case a(2) < a(0) And a(2) < a(1)
      If a(0) < a(1) Then
        c = a(2): b = a(0): a = a(1)
      Else
        c = a(2): b = a(1): a = a(0)
      End If
  End Select
End Function

うーん、マルチステートメントを使ってもなお長いですね。
VBAでは、基本的な決まりとして1ステートメントは1行で書くことになっています。しかし、あまりに長くなってしまうと見づらくなります。逆に、短いステートメントが多数行になっていても見づらい場合もあります。
4値になったら降参するしかない感じです。
エクセルには便利な関数が多数あります。

Function fnc(a, b, c)
  a = Array(a, b, c)
  c = WorksheetFunction.Small(a, 1)
  b = WorksheetFunction.Small(a, 2)
  a = WorksheetFunction.Small(a, 3)
End Function

上記ではSmall関数を使っていますが、配列を変数cに入れてLarge関数を使っても同じです。
また3値だけなので、Max関数とMin関数を使った方法もあるでしょう。

最新のエクセル365ではSort関数が使えます。
SORT関数は、範囲または配列の内容を並べ替えます。SORTBY関数は、範囲または配列を対応する範囲または配列の値に基づいて並べ替えます。SORT関数とSORTBY関数は範囲を並べ替える関数ですが、同じこともできますが、れぞれの関数でなければできないこともあります。

Function fnc(a, b, c)
  a = WorksheetFunction.Sort(Array(a, b, c), , 1, True)
  c = a(LBound(a))
  b = a(LBound(a) + 1)
  a = a(LBound(a) + 2)
End Function

Array関数は横の1次元配列なので、4番目の引数で列方向を指定しています。
TransposeeしてからSortしても良いですが、その場合は2次元配列になるので気を付けてください。


私の回答は以上になります。
お寄せいただいた回答では、さらに様々な方法で取り組まれていますので、ぜひ参考に読んでみてください。

https://twitter.com/search?lang=ja&q=https%3A%2F%2Ftwitter.com%2Fyamaoka_ss%2Fstatus%2F1315298046880620544&src=typed_query





同じテーマ「エクセル雑感」の記事

囲碁で相手の石を囲んで取るアルゴリズム
VBAで「3Lと5Lのバケツで4Lの水を作る」を解く
言語依存の関数を使用できるFormulaLocal
配列のUBoundがLBoundがより小さいことはあり得るか
ショートカット(Ctrl+Shift+n)抜け番ばどれだ
コレクションの要素を削除する場合
入力規則で○△を入れる数を制限する方法
greeenはgreenに、greeeeeNをGReeeeNに変換
数値変数の値を別の変数を使わずに入れ替える
Rangeオブジェクトを受け取り"行数,列数"で埋める
数式の関数の使用回数、関数名を配列で返す


新着記事NEW ・・・新着記事一覧を見る

数式の関数の使用回数、関数名を配列で返す|エクセル雑感(10月19日)
Rangeオブジェクトを受け取り"行数,列数"で埋める|エクセル雑感(10月16日)
数値変数の値を別の変数を使わずに入れ替える|エクセル雑感(10月13日)
WEBスクレイピング(selenium)|Python入門(10月11日)
エクセルを操作する(pywin32:win32com)|Python入門(10月5日)
エクセルを操作する(openpyxl)|Python入門(10月3日)
pipコマンド(外部ライブラリのインストール)|Python入門(10月1日)
CSV読み書き(csvモジュール)|Python入門(9月29日)
「Excel 4.0 マクロ」の使い方|VBA技術解説(9月28日)
CSV読み込みとopen()関数とwith文|Python入門(9月28日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
5.マクロって何?VBAって何?|VBA入門
6.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
7.繰り返し処理(For Next)|VBA入門
8.セルに文字を入れるとは(Range,Value)|VBA入門
9.とにかく書いてみよう(Sub,End Sub)|VBA入門
10.マクロはどこに書くの(VBEの起動)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ