位运算与随机数

位运算

Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:
A = 0011 1100

B = 0000 1101

有:

A&B = 0000 1100

A | B = 0011 1101

A ^ B = 0011 0001

~A= 1100 0011

运算规则:

  • & 如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
  • | 如果相对应位都是 0,则结果为 0,否则为 1 (A | B)得到61,即 0011 1101
  • ^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
  • 〜 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
  • << 按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000
  • [ >> ](带符号位右移)按位右移运算符。左操作数按位右移右操作数指定的位数。 A >> 2得到15即 1111
  • [ >>> ](不带符号位右移)按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。A>>>2得到15即0000 1111

看一到题:实现一个函数:打印一个数的二进制形式(32位),通过测试,我们也发现一个数取反再+1等于这个数的相反数:(~n+1 = -n)

public class PrintBinary {
	/**
	 * 逻辑:数字1 向左移几位,那此位置的二进制一定是1,利用这一点让num 与 其做&运算
	 * &运算逻辑:两者都是1结果才是1,有一个是0结果都是0
	 * 所以,让我们的目标数 和 左移的1 进行与运算,就得到此位置是0还是1
	 *
	 * 又因为,int类型在底层存储是32位,即下标为0~31,
	 * 所以我们用31~0的循环,打印出这个数 32个位置的二进制数
	 */
	public static void print(int num) {
		for (int i = 31; i >= 0; i--) {
			System.out.print((num & (1 << i)) == 0 ? "0" : "1");
		}
		System.out.println();
	}

	public static void main(String[] args) {

//		int c = Integer.MIN_VALUE;
//		int d = -c ;
//
//		print(c);
//		print(d);

    // 测试 一个数取反再+1等于这个数的相反数
		// ~n+1 = -n
		int c = 5;
		int d = -c;

		print(c);
		print(d);
		print(~c);
		print(~c+1);

	}

}

//结果:
00000000000000000000000000000101
11111111111111111111111111111011
11111111111111111111111111111010
11111111111111111111111111111011

随机数

随机数的使用:Math.random() 等概率返回 [0,1) 的一个小数

看下面代码,实现了:等概率返回15,等概率得到0和1,等概率返回06

public class RandToRand {
 
  // 此函数只能用,不能修改
	// 等概率返回1~5
	public static int f() {
		return (int) (Math.random() * 5) + 1;
	}

	// 等概率得到0和1
	public static int a() {
		int ans = 0;
		do {
			ans = f();
		} while (ans == 3);
		return ans < 3 ? 0 : 1;
	}

	// 等概率返回0~6
	public static int b() {
		int ans = 0;
		do {
			// 这个等概率得到[0,7]
			ans = (a() << 2) + (a() << 1) + a();
		} while (ans == 7);
		return ans;
	}
}

题1: 从1-5随机 到 1-7随机

看完以上代码,看题:

从1-5随机 到 1-7随机,已知一个函数可以等概率返回[1,5]中的一个整数,请设计一个返回等概率返回[1,7]中的一个整数

再发散下:从a-b随机 到 c-d随机

解题步骤:

  •  1、利用a-b随机 这个条件 实现一个函数f:等概率返回0,1
    
  •  2、对f函数的返回值,运用位运算实现c-d随机
    
	// lib里的,不能改! 等概率返回[1,5]中的一个整数,一直条件
	public static int f1() {
		return (int) (Math.random() * 5) + 1;
	}

	// 随机机制,只能用f1,
	// 实现:等概率返回0和1
	public static int f2() {
		int ans = 0;
		do {
			ans = f1();
		} while (ans == 3);
		return ans < 3 ? 0 : 1;
	}

	// 利用位运算:得到000 ~ 111 (这是二进制)做到等概率
	// 也就实现了0 ~ 7等概率返回一个
	public static int f3() {
		return (f2() << 2) + (f2() << 1) + f2();
	}

	// 0 ~ 6等概率返回一个
	public static int f4() {
		int ans = 0;
		do {
			ans = f3();
		} while (ans == 7);
		return ans;
	}

	// 等概率得到1~7
	public static int g() {
		return f4() + 1;
	}

	// 或者这种实现:基于f3返回:1 ~ 7等概率返回一个
	public static int f4_2() {
    // f3()实现了0 ~ 7等概率返回一个,所以如果遇到0就重做
		int ans = 0;
		do {
			ans = f3();
		} while (ans == 0);
		return ans;
	}

题2:已知一个函数等概率返回3-19,请你实现一个函数随机返回20-56。

再练习下吧:题2:已知一个函数等概率返回3-19,请你实现一个函数随机返回20-56。

解题:

  • 利用3-19随机,实现一个函数f:等概率返回0,1。思路:3-19共有17个数,所以3-10返回0,12-19返回1,11就继续循环
  • 在刚刚实现的 f() 的基础上,运用位运算的技巧实现20-56随机。思路:20-56随机,就是0-36随机再加上20,36和2的n次方相比较,36<64(2的6次方),所以我执行函数f() 6次可得到0-63随机,如果这个数大于36就重做。

代码:

public class RandToRand {
    // 已知一个函数等概率返回3-19 [3,19]
    public static int f1() {
        return (int) (Math.random() * 17) + 3;
    }

    // 随机机制,只能用f1,
    // 实现:等概率返回0和1
    public static int f2() {
        int ans = 0;
        do {
            ans = f1();
        } while (ans == 11);
        return ans < 11 ? 0 : 1;
    }

    // 利用位运算:得到000000 ~ 111111 做到等概率返回[0,63]
    // 也就实现了0 ~ 63等概率返回一个
    public static int f3() {
        return (f2() << 5) + (f2() << 4) + (f2() << 3) + (f2() << 2) + (f2() << 1) + f2();
    }

    // 0 ~ 36等概率返回一个
    public static int f4() {
        int ans = 0;
        do {
            ans = f3();
        } while (ans > 36);
        return ans;
    }

    // 等概率得到 20~56
    public static int g() {
        return f4() + 20;
    }

    public static void main(String[] args) {
        int testTimes = 10000000;
        int[] counts = new int[57];

        for (int i = 0; i < testTimes; i++) {
            int num = g();
            counts[num]++;
        }
        for (int i = 0; i < 57; i++) {
            System.out.println(i + "这个数,出现了 " + counts[i] + " 次");
        }
    }

}
// 测试结果:随机返回20~56, 等概率返回[20,56]中的一个数
0这个数,出现了 01这个数,出现了 02这个数,出现了 03这个数,出现了 04这个数,出现了 05这个数,出现了 06这个数,出现了 07这个数,出现了 08这个数,出现了 09这个数,出现了 010这个数,出现了 011这个数,出现了 012这个数,出现了 013这个数,出现了 014这个数,出现了 015这个数,出现了 016这个数,出现了 017这个数,出现了 018这个数,出现了 019这个数,出现了 020这个数,出现了 27095121这个数,出现了 27011122这个数,出现了 27042523这个数,出现了 27027524这个数,出现了 26905325这个数,出现了 27053426这个数,出现了 27046127这个数,出现了 27052328这个数,出现了 27001329这个数,出现了 27029130这个数,出现了 26951531这个数,出现了 27047732这个数,出现了 27046833这个数,出现了 27008834这个数,出现了 27031635这个数,出现了 26930536这个数,出现了 27011337这个数,出现了 27062338这个数,出现了 27077739这个数,出现了 27015440这个数,出现了 27101641这个数,出现了 26981442这个数,出现了 26949843这个数,出现了 27021644这个数,出现了 27054245这个数,出现了 26977946这个数,出现了 27028147这个数,出现了 27036648这个数,出现了 27029949这个数,出现了 27033150这个数,出现了 26987851这个数,出现了 27075652这个数,出现了 27073153这个数,出现了 27064454这个数,出现了 27127155这个数,出现了 26948356这个数,出现了 270622

题3: 01不等概率随机 到 01等概率随机

已知 函数x()会以固定概率(不一定是等概率)返回0和1,请实现一个函数:等概率返回0和1

public class EqualProbabilityRandom {
    // 你只能知道,x会以固定概率(不一定是等概率)返回0和1,但是x的内容,你看不到!
    public static int x() {
        return Math.random() < 0.84 ? 0 : 1;
    }

    // 使用x函数实现:等概率返回0和1
    /**
     * 因为x函数是固定概率(不一定是等概率)返回0和1,假设0的概率位p,那1的概率是1-p
     * roll两次x函数,会得到四种可能:00、11、01、10
     * 其中得到10、01的概率是相等的
     */
    public static int y() {
        int ans = 0;
        do {
            ans = x();
        } while (ans == x());
        return ans;
    }

    public static void main(String[] args) {
        int testTimes = 10000000;
        int[] counts = new int[3];

        for (int i = 0; i < testTimes; i++) {
            int num = y();
            counts[num]++;
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(i + "这个数,出现了 " + counts[i] + " 次");
        }
    }

}
// 测试结果:
0这个数,出现了 50019101这个数,出现了 49980902这个数,出现了 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值