一、排列公式(有顺序)
排列公式: ,其中 表示要进行排列的对象总数, 表示要选择的对象个数。
, 其中 n (n-1) (n-2) ... 的个数为 个,从大到小,多余的删除。
或者写成下述方式
其中,(n!) 表示 n 的阶乘,即从 1 到 n 的连续整数的乘积,(n-k)! 表示 (n-k) 的阶乘。
例如:我们要从1、2、3这三个数字中选择2个进行排列(注意不是组合)。
根据排列公式 ,我们得到的结果是6,这表示从1、2、3这三个数字中选择2个进行排列,共有6种可能的情况。这些情况包括:
1-2
1-3
2-1
2-3
3-1
3-2
每对数字的顺序都是不同的,因此总共有6种可能。
二、组合公式(没有顺序)
排列公式: ,其中 表示要进行排列的对象总数, 表示要选择的对象个数。
, 其中 n (n-1) (n-2) ... 的个数为 个,从大到小,多余的删除。
或者写成下述方式
其中,(n!) 表示 n 的阶乘,即从 1 到 n 的连续整数的乘积;(k!) 表示 k 的阶乘;(n-k)! 表示 (n-k) 的阶乘。
特殊性质1: ,
例如1: a=1,b=2,n=3
例如2:a=3,b=4,n=7
特殊性质2:
表示n个对象选取n个,这种集合只有一种,所以等于 1 。
表示n个对象选取0个,空集只有一种,所以等于 1 ; 也有另一种解释 ,所以 ,所以
对于给定的 3 个对象:1、2、3,我们可以计算出不考虑选择几个对象的情况下的总组合数。
通过组合公式,我们可以得到:
将这些组合数相加,得到总共的组合数为:
我们可以观察到,这个结果等于 。这是因为,在每个位置上,我们有两个选择:选择该位置的对象或不选择该位置的对象。在 3 个位置上,共有 种可能性。
三、二项式定理
或
通项公式: , 其中 r 的取值为 0 到 n 。
项数等于 n + 1 。
为项()的系数。
扩展:系数
在数学中,系数是指一个数与某个变量或项之间的乘法关系中的常数因子。它通常用于代数表达式中,表示变量或项与其前面的常数的乘积。
在代数表达式中,常见的形式是ax,其中a是系数,x是变量。系数可以是整数、有理数或实数。它用于表示变量的数量、大小或比例关系。
举例来说,在代数表达式2x^2 + 3x + 1
中,2是 x^2
的系数,3是x
的系数,而1可以看作是常数1与x^0
(也就是常数项)的系数。
系数在代数中起着重要的作用,它们决定了变量或项在表达式中的贡献和权重。通过改变系数的值,可以调整变量或项对整体表达式的影响程度。系数也可以用于解方程、求导、展开多项式等数学运算中。
扩展1:求和 ( )
符号 是用来表示求和的。在数学中,它表示我们将一系列的项相加求和。这些项可以按照上下文的要求进行加法或减法运算。
例如,如果我们有一系列的数字 ,那么这些数字的和可以用求和符号表示为 。这意味着我们要对所有的项 (其中 从1到 )进行求和。
求和符号中的索引表示要求和的项在序列中的位置。在上面的例子中, 的取值范围是从1到 ,因此我们要对序列中的所有项进行求和。
求和的结果是一个单一的值,它是所有项相加或相减的和。这个值是按照加法和减法的规则得到的。
求和符号也可以在符号下面或上面指定额外的条件或约束。例如, 表示我们对从1到 的项 进行求和。如果有额外的约束条件,它们被称为求和的限制。
总的来说,求和符号是数学中用来表示一系列项的总和的一种便捷的记号。它被广泛应用于各个数学分支,包括代数、微积分和数论。
扩展2:
的另一种常见写法是。这种写法使用了二项式系数的表示方法。
在组合数学中,表示从个元素中取个元素的组合数,也就是从个元素中选取个元素的可能性的数量。
使用的写法,可以更清晰地表示出这种组合数的意义。在这个写法中,表示从中选择个元素的组合数。
这里的读作"n choose k",它是用来表示组合数的一种常见记号。可以通过以下公式进行计算:
其中表示的阶乘,即。
这种写法更加直观地表示了组合数的含义,同时也更加符合数学符号的常规用法。所以,在组合数学中,是较常用的表示方法之一。
扩展3:
(-1)^N的结果取决于奇偶性是由数学规律决定的。
当指数为偶数时,(-1)^N的结果为1。
当指数为奇数时,(-1)^N的结果为-1。
简而言之,(-1)^N的值取决于N的奇偶性。
如果N是偶数,(-1)^N等于1;
如果N是奇数,(-1)^N等于-1。
解释:
当一个数的指数是偶数时,例如2、4、6等,那么(-1)的任何偶次幂得到的结果都是1。这是因为在乘法中,负数乘以负数得到正数,即(-1) * (-1) = 1。当偶次幂相乘时,每个(-1)都会被消除,最终结果为1。
而当一个数的指数是奇数时,例如1、3、5等,那么(-1)的奇次幂得到的结果都是-1。这是因为在乘法中,负数乘以负数得到正数,即(-1) * (-1) = 1。当奇次幂相乘时,仍然存在一个(-1)无法被消除,最终结果为-1。
因此,根据乘法的规则以及(-1)的性质,(-1)^N的结果取决于N的奇偶性。
负数的幂运算可以通过将其转化为倒数和正数的幂运算来处理。具体来说,对于一个实数 a 和一个整数 n:
当 n 是偶数时,(-a)^n = a^n。
当 n 是奇数时,(-a)^n = -a^n。
这是因为负数的偶次幂得到的结果是正数,而负数的奇次幂得到的结果是负数。
需要注意的是,如果 n 是分数或者实数,负数的幂运算并没有明确的定义,因为它涉及到复数和复数幂的概念。在复数领域中,涉及到幅角、辐角、共轭复数等概念来描述负数的幂运算。
综上所述,负数的幂运算涉及到指数的奇偶性,并且结果可能是正数或负数,具体要根据指数的奇偶性来确定。
任何数的零次方都等于 1 。
扩展4: 二项式展开
当n=5, 奇数。
当n=6, 偶数。
所以二项式系数:
如有错误欢迎指正,谢谢!
参考:
https://www.bilibili.com/video/BV1Yq4y1E7JF/?spm_id_from=333.337.search-card.all.click&vd_source=fd551c73306d10ac7c5d19c2369e1ce3
https://www.bilibili.com/video/BV1pb4y1X7AA/?spm_id_from=333.337.search-card.all.click&vd_source=fd551c73306d10ac7c5d19c2369e1ce3
def subsetXORSum(nums: list[int]) -> int:
res = 0
n = len(nums)
# nums集合中 元素的二进制某一位上有1的位置才会影响最终结果
# 汇总nums集合中所有元素二进制上所有的1
for num in nums:
res |= num
# n个元素整个集合的大小(含空集)是2^n
# 根据数学推论,二进制某一位上有1的元素,在最终的集合中,一半0一半1
# 所以把前面的汇总结果乘以2^n / 2,即2^(n-1)
# res << (n-1) 等于 res * 2^(n-1)
return res << (n - 1)
#nums = [1, 3]
#nums = [5, 1, 6]
nums = [3, 4, 5, 6, 7, 8]
print(subsetXORSum(nums))
以下是对上一段脚本的解释:
例1
答案:
110 <- 6
nums集合内的元素:
001 <- 1
011 <- 3
所有组合:
000 <- 空
001 <- 1
011 <- 3
010 <- 2 <- 1^3
6
# 在上上述组合中出现的次数。
001 * 2(2**(2-1)) = 2
010 * 2(2**(2-1)) = 4
11 <- 3 <- (1 | 3)
110 <- 6 <- 3<<(2-1)
------------------------------------------------
例2
答案:
1100 <- 12
nums集合内的元素:
001 <- 1
010 <- 2
011 <- 3
所有组合(2**3=8):
000 <- 空
001 <- 1
010 <- 2
011 <- 3
011 <- 3 <- 1^2
010 <- 2 <- 1^3
000 <- 0 <- 1^2^3
001 <- 1 <- 2^3
12
010 * 4(2**(3-1))
001 * 4(2**(3-1))
11 <- 3 <- (1 | 2 | 3)
1100 <- 12 <- 3<<(3-1)
-----------------------------------------
例3
答案:28
11100
nums集合内的元素:
00001 <- 1
00100 <- 4
00110 <- 6
所有组合(2**3=8):
00000 <- 空
00001 <- 1
00100 <- 4
00110 <- 6
00101 <- 5 <- 1^4
00111 <- 7 <- 1^6
00011 <- 3 <- 1^4^6
00010 <- 2 <- 4^6
28
00001 * 4(2**(3-1))
00010 * 4(2**(3-1))
00100 * 4(2**(3-1))
111 <- 7 <- (1 | 4 | 6)
11100 <- 28 <- 7<<(3-1)
通过上述3个例子,可以得出下面的结论:
nums集合中有 n 元素,它的所有子集的异或总和为 它的所有元素的 或 结果 位运算向左位移(n-1)。或者说结果乘以 2的(n-1) 次方。
nums = [a1, a2, a3, ...]
nums 所有子集的异或总和 = (a1 | a2 | a3 ...) << (n-1) = (a1 | a2 | a3 ...) * 2**(n-1)
例如:
nums = [1, 4, 6]
n = len(nums)
res = 0
for L in nums:
res = res | L
res = res << (n-1)
print(nums, '所有子集的异或总和为:', res)
一个数组的所有组合有 2^n 种(包括一个空集),n 为数组中元素的个数。
对于一个正数,它的每个二进制位都是 0 或 1。
如果数组中所有元素该位都是0,最终每种组合所有数该位必为0,异或和贡献也为0;
如果数组中所有元素该位有一个或多个1时,则最终贡献必有一半是1。(另一半是0)。
所以数组中元素的 或操作 结果, 就是数组中所有元素的二进制为1的结果,
然后乘以2^(n -1)即可得出数组的所有子集的 异或结果的总和。
为什么乘以 2^(n-1) ,因为一个元素或多个元素的二进制某位为1,它在所有组合中异或结果的二进制中该位出现1的次数为所有 组合( 2^n ) 的一半。
即 2^n / 2 = 2^(n-1)
一个整数 L, L * 2^(n-1) = L << (n-1)
尝试解释这个等式:
在十进制中 L * 10^m = L << m (L的十进制向左移动m位然后补m个0。)
L=3,m=3 3 * 10^3 = 3000
3 * 1000 = 3000
3000 = 3000
同理在二进制中 L * 2^m = L << m (L的二进制向左移动m位然后补m个零。)
L=3,m=3 3 * 2^3 = 11 << 3
3 * 8 = 11000
24 = 24
例: 组合中元素分别为 1, 4, 6 ,n = 3。
001 <- 1
100 <- 4
110 <- 6
111
即 001 * 2^(3-1) + 010 * 2^(3-1) + 100 * 2^(3-1) (这里的 001, 010, 100 是二进制)
= 1 * 4 + 2 * 4 + 4 * 4
= 28
或 001 << (3-1) + 010 << (3-1) + 100 << (3-1) (这里的 001, 010, 100 是二进制)
= 00100 + 0100 + 10000
= 4 + 8 + 16
= 28
对于力扣官方关于这种解法的解释,目前我还无法彻底理解并清晰地解释出来。在我思考透彻后,我会进行更新。如果有任何能够帮助解释的人,欢迎指正和提供帮助。