<《アルゴリズムが美しい>>-(1)一般的なビット演算

コンテンツ

1.ビット演算とベース

2.いくつかの興味深いビット演算

3. n&(n-1)を使用します

1つの問題と3つの解決策:2進数の1の数

数値が2の指数であるかどうかを判別します

小さなテスト

4. 运用a^a=0

注文数を探す

小さなテスト

5.ビット演算の考え方を改善する

整数のパリティビットを交換します

浮動小数点実数

K回発生し、1回発生します


1.ビット演算とベース

ビット演算の奇妙なトリック:

  • パリティの判断
  • バイナリビットの取得は1または0です
  • 2つの整数変数の値を交換します
  • 判断ステートメントを使用せずに整数の絶対値を見つける
  • >>および<<演算子はビットを右または左にシフトします
  • >>>演算子は上位ビットを0で埋めます;>>演算子は上位ビットを符号ビットで埋めます、<<<演算子はありません
  • intの場合、1<<35は1<<3と同じです。
  • AND:両方が1、結果が1;または:1が1、結果が1; XOR:2つが同じではない、結果が1
    a b 〜a a&b a | b a ^ b
    1 1 0 1 1 0
    0 1 1 0 1 1
    0 0 1 0 0 0

2.いくつかの興味深いビット演算

1.または操作とスペースを使用して、 | 英語の文字を小文字に変換します


('a' | ' ') = 'a'
('A' | ' ') = 'a'

2.  AND演算 & とアンダースコアを使用して、英字を大文字に変換します

('b' & '_') = 'B'
('B' & '_') = 'B'

3. XOR演算 ^ とスペースを使用して、英字の大文字と小文字を交換します

('d' ^ ' ') = 'D'
('D' ^ ' ') = 'd'

4.2つの数字が同じ符号であるかどうかを判断します

int x = -1, y = 2;
boolean f = ((x ^ y) < 0); // true

int x = 3, y = 2;
boolean f = ((x ^ y) < 0); // false

5.一時変数なしで2つの数値を交換します

int a = 1, b = 2;
a ^= b;
b ^= a;
a ^= b;
// a = 2, b = 1

6.1つ追加します

int n = 1;
n = -~n;
//  n = 2

7.マイナス1

int n = 2;
n = ~-n;
//  n = 1

 ps:上記の操作は、友達の前のカップにも使用できますが、実用的ではありません。

3. n&(n-1)を使用します

n &(n-1) この操作はアルゴリズムで一般的で あり、数値n の2進表現の最後の1を削除するように機能します。

コアロジックはn - 1 、最後の1を削除すると同時に、後続の0を1に変更して、合計 演算がn 再度実行され & 、最後の1のみを0に変換できるようにする必要があるということです。

使用方法の例を次に示します。

1つの問題と3つの解決策:2進数の1の数

 

 方法1:整数nはビット単位で1、つまりn&1(ビット単位で:結果は1で1)、nが2進数で右端の1として表される場合、n&1は1、count ++は1回、次にn右に移動>>1つずつ比較します。

コードに直接移動します。

#include<stdio.h>
int main()
{
	int n;
	int ants = 0;
	scanf("%d",&n);
	for (int i = 0; i < 32; i++)
	{
		int m = n;
		if ((m>>i)&1== 1)
			ants++;
	}
	printf("%d", ants);
	return 0;
}

方法2:方法と同様です。つまり、整数nを変更せずに、1を左に移動します。ここではコードが提供されていないため、試してみることに興味があります。

方法3:上記で説明したn&n(-1)を使用しますが、ここでは詳しく説明しません。上記を確認できます。

int hammingWeight(int n) {
    int res = 0;
    while (n != 0) {
        n = n & (n - 1);
        res++;
    }
    return res;
}

数値が2の指数であるかどうかを判別します

 

数値が2の指数である場合、その2進表現には1のみが含まれている必要があります。 

2^0 = 1 = 0b0001
2^1 = 2 = 0b0010
2^2 = 4 = 0b0100

使用する手法が非常に単純な場合 n & (n-1) (演算子の優先順位に注意してください。括弧は省略できません)。

boolean isPowerOfTwo(int n) {
    if (n <= 0) return false;
    return (n & (n - 1)) == 0;
}

小さなテスト

>>>ビット1の数

>>>インデックス2(タイトルコードは上に表示されています。ご不明な点がございましたら、コメント欄でご相談ください)

4. 运用a^a=0

XOR演算の性質は、次の点に注意する必要があります。

数値とそれ自体の間のXOR演算の結果は0、つまり a ^ a = 0;数値と0の間のXOR演算の結果はそれ自体、つまり a ^ 0 = a

注文数を探す

この質問では、すべての数値をXORする限り、ペアの数値は0になります。単一の数値と0のXORはそれ自体であるため、最終的なXORの結果は1回だけ現れる要素です。

int singleNumber(int[] nums) {
    int res = 0;
    for (int n : nums) {
        res ^= n;
    }
    return res;
}

小さなテスト

>>>一度だけ表示される番号

5.ビット演算の考え方を改善する

整数のパリティビットを交換します

ここでの整数のパリティスワップは、実際にはバイナリのパリティビットのスワップです。10進数の15のパリティスワップの代わりに、51に置き換えます。たとえば、1010を0101に置き換えます。

アイデア:整数を入力し、ビット単位で010101 ...偶数の値をou(この文には好みが必要)として保持し、次にビット単位で101010 ...奇数の値をjiとして保持し、ouを移動します左に<<1ビット、右にji >> 1ビット、次にXORして目的の目的を達成します。 

コード:

#include<stdio.h>
int main()
{
	int n;
	scanf("%d",&n);
	int ji = n & 0xaaaaaaaa;//1010...
	int ou = n & 0x55555555;//0101...
    int c= (ou << 1) ^(ji >> 1);
	printf("%d",c);
	return 0;
}

浮動小数点実数の2進表現

コード:

0.625 0.5+0.125
0.101
#include<iostream>
using namespace std;
int main()
{
	double n;
	cin >> n;
	string s1 = "0.";
	while (n > 0)
	{
		n= n * 2;
		if (n >=1)
		{
			s1 +="1";
			n = n - 1;
		}
		else {
			s1 += "0";
			n = n;
		}
		if (s1.size() > 34)
		{
			cout << "ERROR " << endl;
		}
	}
	cout << s1 << endl;
	return 0;
}

K回発生し、1回発生します

問題解決のブレークスルー:k個のK-ary数を使用して、キャリーなしで加算することにより、結果が0であるという結論

最初に10進数をk-aryに変換し、文字列を使用して逆の順序で格納します。
余りのkで除算する方法(一般的な計算方法)を使用して、配列内の数値をk-ary番号に変換します。

k-aryに変換した後、ビット(列)で計算する必要があるため、文字列の形式で格納されます。k-aryシステムに変換した後、数値のサイズが異なるため、長さも異なる場合があります.k個の数値の合計は0ですが、違いはありませんが、ビットシーケンスは必要なものにとって非常に重要です数値(計算文字列を左から右に計算する場合、および10進数に戻す場合は右から左に計算する)、したがって、kベースに変換された数値は逆の順序で格納されます。

文字列の最大長を見つけて、maxlen未満の文字列を入力します。
列ごとに計算する必要があるため、最大で何列あるか、つまりmaxlenを知る必要があります。キャリーなしでビットごとの加算を行うために、上位に「0」を入力し、maxlenの長さを構成します

キャリーなしで
加算これらの数値をキャリーなしで加算します。これはXOR演算^と同等です。または、kを法とするビット単位の加算の後に数値を取ります。

出力結果、K-aryからdecimal
このステップは単純で、一般的な8進数から10進数および16進数から10進数と同じ原理です。

#include<iostream>
#include<algorithm>
#include<string>
//#include<cmath>
using namespace std;
string decTok(int dec, int k); //十进制数转K进制 
int kTodec(string str, int k); // K进制转十进制

string decTok(int dec, int k) 
{
	string ret = "";  //作为结果
	while (dec > 0) {
		ret += char(dec%k + '0');//如:5+'0'='5' 
		dec /= k;
	}
	reverse(ret.begin(), ret.end());//翻转
	return ret;
}

int kTodec(string str, int k) 
{
	int ans = 0;
	for (int i = 0; i < str.size(); i++)
		ans = ans * k + (str[i] - '0');//020 首位是最高位 
	return ans;
}

int main() 
{
	int n[] = { 1,1,1,3,3,3,5,5,5,9,9,9,6,7,7,7 };
	int k = 3; //根据数组数据,要转换的进制 

	//1.十进制数转K进制 
	string str[16];
	for (int i = 0; i < 16; i++)
		str[i] = decTok(n[i], k);

	//2.找出16条字符串中最大长度
	int maxlen = 0;
	int len;
	for (int i = 0; i < 16; i++) {
		len = str[i].size();
		maxlen = max(maxlen, len); //maxlen=max(maxlen,str[i].size())
	}
	//16条字符串中,若字符串长度<maxlen,则进行补齐,以便逐位做不进位加法
	for (int i = 0; i < 16; i++)
		while (str[i].size() < maxlen)
			str[i] = "0" + str[i];

	//ans:结果初始化 
	string ans = "";
	while (ans.size() < maxlen)
		ans += "0";

	//3.做不进位加法
	for (int i = 0; i < 16; i++)
		for (int j = 0; j < maxlen; j++)
			ans[j] = char(((str[i][j] - '0') + (ans[j] - '0')) % k + '0');

	cout << ans << endl; //ans字符串结果
	cout<< kTodec(ans, k); //转为十进制数 
	return 0;
}

おすすめ

転載: blog.csdn.net/m0_58367586/article/details/123705689