dポイントプレゼントキャンペーン実施中!

現在
「2バイトの数値」から「ビットが1である数」を求める
計算を行おうと思っています。

例えば、2バイトの数値が「222」の場合
→ビットに直すと「11011110」となり
→ビットが1である数は「6」となります。

この計算をVBで行いたいのですが、
効率的に行える方法はないでしょうか?

AND演算を行い1ビットずつカウントすると
いう方法を考えたのすが、判定の時IF文がはいってしまうため
処理速度が遅くなり、困っております。

どなたか、お気づきの方はご教授ください。

A 回答 (7件)

こんにちは。

maruru01です。

ちょっと強引な方法ですが。
処理速度は確かめていません。


Dim temp As Long
Dim tempOct As String
Dim i As Long
Dim cnt As Long

temp = 222
tempOct = Oct(temp)
cnt = 0
For i = 1 To Len(tempOct)
  cnt = cnt + CLng(Mid("01121223", Mid(tempOct, i, 1) + 1, 1))
Next i
MsgBox cnt
    • good
    • 0

教科書に書いてある10進->2進変換の通常方法の、2で割る方法はダメですか。

エクセルVBAでテストしました。カウントする変数にn Mod 2が1であれば1を足せば良いのでは。これだと判ってるわいといわれそうですが。
技術計算や数値解析の繰り返し計算の膨大なルーチンですか。「判定の時IF文がはいってしまうため処理速度が遅くなり」-今時のコンピューターで信じられない。
Sub test01()
n = Cells(1, 1)
s = "" '2進数文字列を表示するため
b = 0 '1のビットの数
For i = 1 To 16
If (n Mod 2) = 1 Then b = b + 1
s = (n Mod 2) & s
n = Int(n / 2)
If n < 2 Then GoTo p01
Next i
p01:
If (n Mod 2) = 1 Then b = b + 1
s = (n Mod 2) & s
MsgBox s
MsgBox b
End Sub
    • good
    • 0
この回答へのお礼

お礼が遅れて申し訳ございません。
皆様にまとめてお礼を申し上げさせていただきます。
たくさんの方から回答いただき感激です。
大変参考になりました。

ありがとうございます。

お礼日時:2003/10/27 10:39

あ、2byte整数ね。



Private Declare Sub MoveMemory Lib "kernel32.dll" _
Alias "RtlMoveMemory" _
(Destination As Any, _
Source As Any, _
ByVal Length As Long)

Function BitCnt(ByVal intVal As Integer) As Long

Dim Cnt As Long
Dim lngVal As Long

Call MoveMemory(lngVal, intVal, 2) 'int型をlong型にキャスト

Do While lngVal
Cnt = Cnt + (lngVal And 1)
lngVal = lngVal \ 2
Loop
BitCnt = Cnt
End Function
    • good
    • 0

bitシフトもどきの


n = n \ 2
を使う。
最上位bitをマスクする必要がある。


Function BitCnt(ByVal n As Long) As Long

Dim Cnt As Long
If (n And &H80000000) Then
Cnt = 1
n = (n And &H7FFFFFFF)
End If
Do While n
Cnt = Cnt + (n And 1)
n = n \ 2
Loop
BitCnt = Cnt
End Function

# int型で扱うよりはlong型のほうが早いかも
    • good
    • 0

0~65535までの入力の場合



i = (i And &HAAAA) / 2 + (i And &H5555)
i = (i And &HCCCC) / 4 + (i And &H3333)
i = (i And &HF0F0) / 16 + (i And &HF0F)
i = (i And &HFF00) / 256 + (i And &HFF)

アセンブラ向きです。
本来は割り算でなくシフト演算使いますが、.NET以降でないと使えないようでしたので。

最初の式が一回の足し算で実質的に八回の足し算をおこなっているのがポイントです。
これは最初は1bit + 1bit = 2bitとなる計算なので
16bit幅があると2bitの演算を8つ分しています。
次は 2bit + 2bit = 4bit (3bitで十分ですが)
以下同様

条件分岐無し、データのメモリアクセス無し、加算、and、シフトは通常アセンブラ最速の命令ということで、
アセンブラレベルならこの手の方法が多分最速です。
VBだとよくわかりませんが、速い事は確かでしょう。

以前、32bit版をCで使いました。
    • good
    • 0

処理速度最優先で何回も行うのでしたら、表引きがいいです。


2バイトの値→ビット1の個数の表をあらかじめ
配列で作成しておき、単純に表を参照するだけです。

実際には、2バイト用の表は結構大きいので、1バイト用の
表を作成して、上位バイト、下位バイトで2回参照するのが
お勧めかもしれません。
(#1の方のも3ビット毎の表引きですね)

実装の例(動作未確認)
private bittab(255) as integer

public function bitcount(byval a as long) as integer
・bitcount = bittab(a mod 256) + bittab(a \ 256)
end function

bitcountを使用する前に、bittab()に0~255まで答えを
初期値として設定しておく必要があります。
(初期値を計算する方法は、従来のシフトなど好きな処理で)
    • good
    • 0

好ましいやり方ではないけれども、IFは使いません。



Dim lngTarget As Long
Dim lngResult As Long

lngResult = ((lngTarget And &H1) <> 0) _
+ ((lngTarget And &H2) <> 0) _
+ ((lngTarget And &H4) <> 0) _
+ ((lngTarget And &H8) <> 0) _
+ ((lngTarget And &H10) <> 0) _
+ ((lngTarget And &H20) <> 0) _
+ ((lngTarget And &H40) <> 0) _
+ ((lngTarget And &H80) <> 0) _
+ ((lngTarget And &H100) <> 0) _
+ ((lngTarget And &H200) <> 0) _
+ ((lngTarget And &H400) <> 0) _
+ ((lngTarget And &H800) <> 0) _
+ ((lngTarget And &H1000) <> 0) _
+ ((lngTarget And &H2000) <> 0) _
+ ((lngTarget And &H4000) <> 0) _
+ ((lngTarget And &H8000) <> 0)

Debug.Print lngResult / (True)

もうひとつ、あらかじめテーブルを用意しておくやり方も。

Dim lngTarget As Long
Dim intBits(256) As Integer

intBits(0) = 0
intBits(1) = 1
intBits(2) = 1
intBits(3) = 2
'...略
intBits(255) = 8

lngTarget = lngTarget And &HFFFF&
Debug.Print intBits(lngTarget \ 256) + intBits(lngTarget Mod 256)
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!