区块链-可编程支付原理

摘自https://liaoxuefeng.com/books/blockchain/bitcoin/pay/index.html

区块链创建交易的方法是:小明声称他给了小红一万块钱,只要能验证这个声明确实是小明作出的,并且小明真的有1万块钱,那么这笔交易就被认为是有效的

如何验证这个声明确实是小明作出的呢?数字签名就可以验证这个声明是否是小明做的,并且,一旦验证通过,小明是无法抵赖的。

在比特币交易中,付款方就是通过数字签名来证明自己拥有某一笔比特币,并且,要把这笔比特币转移给指定的收款方

比特币的公钥是根据私钥计算出来的

比特币的地址并不是公钥,而是公钥的哈希,即从公钥能推导出地址,但从地址不能反推公钥,因为哈希函数是单向函数

签名算法是使用私钥签名,公钥验证的方法,对一个消息的真伪进行确认。如果一个人持有私钥,他就可以使用私钥对任意的消息进行签名,即通过私钥sk对消息message进行签名,得到signature

signature = sign(message, sk);

签名的目的是为了证明,该消息确实是由持有私钥sk的人发出的,任何其他人都可以对签名进行验证。验证方法是,由私钥持有人公开对应的公钥pk,其他人用公钥pk对消息message和签名signature进行验证:

isValid = verify(message, signature, pk);

如果验证通过,则可以证明该消息确实是由持有私钥sk的人发出的,并且未经过篡改。

对消息进行签名,实际上是对消息的哈希进行签名,这样可以使任意长度的消息在签名前先转换为固定长度的哈希数据。对哈希进行签名相当于保证了原始消息的不可伪造性。

下面来说可编程支付的原理

比特币的所有交易的信息都被记录在比特币的区块链中,任何用户都可以通过公钥查询到某个交易的输入和输出金额。当某个用户希望花费一个输出时,例如,小明想要把某个公钥地址的输出支付给小红,他就需要使用自己的私钥对这笔交易进行签名,而矿工验证这笔交易的签名是有效的之后,就会把这笔交易打包到区块中,从而使得这笔交易被确认。

但比特币的支付实际上并不是直接支付到对方的地址,而是一个脚本,这个脚本的意思是:谁能够提供另外一个脚本,让这两个脚本能顺利执行通过,谁就能花掉这笔钱:

FROM: UTXO Hash#index
AMOUNT: 0.5 btc
TO: OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG

所以,比特币交易的输出是一个锁定脚本(一般是资产转入方地址),而下一个交易的输入是一个解锁脚本(里面包括资产转入方的公钥和签名)。必须提供一个解锁脚本,让锁定脚本正确运行,那么该输入有效,就可以花费该输出。

我们以真实的比特币交易为例,某个交易的某个输出脚本是76a914dc...489c88ac这样的二进制数据,注意这里的二进制数据是用十六进制表示的,而花费该输出的某个交易的输入脚本是48304502...14cf740f这样的二进制数据,也是十六进制表示的:

   ┌─────────────────────────────────────────────────────────────────────┐
   │tx:ada3f1f426ad46226fdce0ec8f795dcbd05780fd17f76f5dcf67cfbfd35d54de  │
   ├──────────────────────────────────┬──────────────────────────────────┤
   │                                  │1M6Bzo23yqad8YwzTeRapGXQ76Pb9RRJYJ│──┐
   │                                  ├──────────────────────────────────┤  │
   │                                  │18gJ3jeLdMnr9g3EcbRzXwNssYEN5yFHKE│  │
   │3JXRVxhrk2o9f4w3cQchBLwUeegJBj6BEp├──────────────────────────────────┤  │
   │                                  │1A5Mp8jHcMJEqZUmcsbmtqXfsiGdWYmp6y│  │
   │                                  ├──────────────────────────────────┤  │
   │                                  │3JXRVxhrk2o9f4w3cQchBLwUeegJBj6BEp│  │
   └──────────────────────────────────┴──────────────────────────────────┘  │
┌───────────────────────────────────────────────────────────────────────────┘
│   script: 76a914dc5dc65c7e6cc3c404c6dd79d83b22b2fe9f489c88ac
│
│  ┌─────────────────────────────────────────────────────────────────────┐
│  │tx:55142366a67beda9d3ba9bfbd6166e8e95c4931a2b44e5b44b5685597e4c8774  │
│  ├──────────────────────────────────┬──────────────────────────────────┤
└─>│1M6Bzo23yqad8YwzTeRapGXQ76Pb9RRJYJ│13Kb2ykVGpNTJbxwnrfoyZAwgd4ZpXHv2q│
   └──────────────────────────────────┴──────────────────────────────────┘
    script: 4830450221008ecb5ab06e62a67e320880db70ee8a7020503a055d7c45b7
            3dcc41adf01ea9f602203a0d8f4314342636a6a473fc0b4dd4e49b62be28
            8f0a4d5a23a8f488a768fa9b012103dd8763f8c3db6b77bee743ddafd33c
            969a99cde9278deb441b09ad7c14cf740f

我们先来看锁定脚本,锁定脚本的第一个字节76翻译成比特币脚本的字节码就是OP_DUPa9翻译成比特币脚本的字节码就是OP_HASH16014表示这是一个20字节的数据,注意十六进制的14换算成十进制是20,于是我们得到20字节的数据。最后两个字节,88表示字节码OP_EQUALVERIFYac表示字节码OP_CHECKSIG,所以整个锁定脚本是:

        OP_DUP 76
    OP_HASH160 a9
          DATA 14 (dc5dc65c...fe9f489c)
OP_EQUALVERIFY 88
   OP_CHECKSIG ac

我们再来看解锁脚本。解锁脚本的第一个字节48表示一个72字节长度的数据,因为十六进制的48换算成十进制是72。接下来的字节21表示一个33字节长度的数据。因此,该解锁脚本实际上只有两个数据。

DATA 48 (30450221...68fa9b01)
DATA 21 (03dd8763...14cf740f)

接下来,我们就需要验证这个交易是否有效。要验证这个交易,首先,我们要把解锁脚本和锁定脚本拼到一块,然后,开始执行这个脚本:

          DATA 48 (30450221...68fa9b01)
          DATA 21 (03dd8763...14cf740f)
        OP_DUP 76
    OP_HASH160 a9
          DATA 14 (dc5dc65c...fe9f489c)
OP_EQUALVERIFY 88
   OP_CHECKSIG ac

比特币脚本是一种基于栈结构的编程语言,所以,我们要先准备一个空栈,用来执行比特币脚本。然后,我们执行第一行代码,由于第一行代码是数据,所以直接把数据压栈:

│                   │
│                   │
│                   │
│                   │
│                   │
│                   │
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

紧接着执行第二行代码,第二行代码也是数据,所以直接把数据压栈:

│                   │
│                   │
│                   │
│                   │
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

接下来执行OP_DUP指令,这条指令会把栈顶的元素复制一份,因此,我们现在的栈里面一共有3份数据:

│                   │
│                   │
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

然后,执行OP_HASH160指令,这条指令会计算栈顶数据的hash160,也就是先计算SHA-256,再计算RipeMD160。对十六进制数据03dd8763f8c3db6b77bee743ddafd33c969a99cde9278deb441b09ad7c14cf740f计算hash160后得到结果dc5dc65c7e6cc3c404c6dd79d83b22b2fe9f489c,然后用结果替换栈顶数据:

│                   │
│                   │
├───────────────────┤
│dc5dc65c...fe9f489c│
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

接下来的指令是一条数据,所以直接压栈:

├───────────────────┤
│dc5dc65c...fe9f489c│
├───────────────────┤
│dc5dc65c...fe9f489c│
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

然后,执行OP_EQUALVERIFY指令,它比较栈顶两份数据是否相同,如果相同,则验证通过,脚本将继续执行,如果不同,则验证失败,整个脚本就执行失败了。在这个脚本中,栈顶的两个元素是相同的,所以验证通过,脚本将继续执行:

│                   │
│                   │
│                   │
│                   │
├───────────────────┤
│03dd8763...14cf740f│
├───────────────────┤
│30450221...68fa9b01│
└───────────────────┘

最后,执行OP_CHECKSIG指令,它使用栈顶的两份数据,第一份数据被看作公钥,第二份数据被看作签名,这条指令就是用公钥来验证签名是否有效。根据验证结果,成功存入1,失败存入0

│                   │
│                   │
│                   │
│                   │
│                   │
│                   │
├───────────────────┤
│1                  │
└───────────────────┘

最后,当整个脚本执行结束后,检查栈顶元素是否为0,如果不为0,那么整个脚本就执行成功,这笔交易就被验证为有效的。

上述代码执行过程非常简单,因为比特币的脚本不含条件判断、循环等复杂结构。上述脚本就是对输入的两个数据视作签名和公钥,然后先验证公钥哈希是否与地址相同,再根据公钥验证签名,这种标准脚本称之为P2PKH(Pay to Public Key Hash)脚本。

输出

当小明给小红支付一笔比特币时,实际上小明创建了一个锁定脚本,该锁定脚本中引入了小红的地址。要想通过解锁脚本花费该输出,只有持有对应私钥的小红才能创建正确的解锁脚本(因为解锁脚本包含的签名只有小红的私钥才能创建),因此,小红事实上拥有了花费该输出的权利。

使用钱包软件创建的交易都是标准的支付脚本,但是,比特币的交易本质是成功执行解锁脚本和锁定脚本,所以,可以编写各种符合条件的脚本。比如,有人创建了一个交易,它的锁定脚本像这样:

OP_HASH256
      DATA 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
  OP_EQUAL

这有点像一个数学谜题。它的意思是说,谁能够提供一个数据,它的hash256等于6fe28c0a...,谁就可以花费这笔输出。所以,解锁脚本实际上只需要提供一个正确的数据,就可以花费这笔输出。点这里查看谁花费了该输出。

比特币的脚本通过不同的指令还可以实现更灵活的功能。例如,多重签名可以让一笔交易只有在多数人同意的情况下才能够进行。最常见的多重签名脚本可以提供3个签名,只要任意两个签名被验证成功,这笔交易就可以成功。

FROM: UTXO Hash#index
AMOUNT: 10.5 btc
TO: P2SH: OP_2 pk1 pk2 pk3 OP_3 OP_CHECKMULTISIG

也就是说,3个人中,只要任意两个人同意用他们的私钥提供签名,就可以完成交易。这种方式也可以一定程度上防止丢失私钥的风险。3个人中如果只有一个人丢失了私钥,仍然可以保证这笔输出是可以被花费的。

支付的本质

从比特币支付的脚本可以看出,比特币支付的本质是由程序触发的数字资产转移。这种支付方式无需信任中介的参与,可以在零信任的基础上完成数字资产的交易,这也是为什么数字货币又被称为可编程的货币。

由此催生出了智能合约:当一个预先编好的条件被触发时,智能合约可以自动执行相应的程序,自动完成数字资产的转移。保险、贷款等金融活动在将来都可以以智能合约的形式执行。智能合约以程序来替代传统的纸质文件条款,并由计算机强制执行,将具有更低的信任成本和运营成本。

小结

比特币采用脚本的方式进行可编程支付:通过执行解锁脚本确认某个UTXO的资产可以被私钥持有人转移给其他人。

贝叶斯定理(原理篇)

转自https://liaoxuefeng.com/blogs/all/2023-08-27-bayes-explain/index.html
可以不用看文章,直接看油管上视频Bayes’ Theorem 贝叶斯定理

托马斯·贝叶斯(Thomas Bayes)是18世纪的英国数学家,也是一位虔诚的牧师。据说他为了反驳对上帝的质疑而推导出贝叶斯定理。贝叶斯定理是一个由结果倒推原因的概率算法,在贝叶斯提出这个条件概率公式后,很长一段时间,大家并没有觉得它有什么作用,并一直受到主流统计学派的排斥。直到计算机诞生后,人们发现,贝叶斯定理可以广泛应用在数据分析、模式识别、统计决策,以及最火的人工智能中,结果,贝叶斯定理是如此有用,以至于不仅应用在计算机上,还广泛应用在经济学、心理学、博弈论等各种领域,可以说,掌握并应用贝叶斯定理,是每个人必备的技能。

这里推荐两个视频,深入浅出地解释了贝叶斯定理:

Bayes’ Theorem 贝叶斯定理

Bayes theorem, the geometry of changing beliefs

如果你不想花太多时间看视频,可以继续阅读,我把视频内容编译成文字,以便快速学习贝叶斯定理。

为了搞明白贝叶斯定理究竟要解决什么问题,我们先看一个现实生活的例子:

已知有一种疾病,发病率是0.1%。针对这种疾病的测试非常准确:

  • 如果有病,则准确率是99%(即有1%未检出阳性);
  • 如果没有病,则误报率是2%(即有2%误报为阳性)。

现在,如果一个人测试显示阳性,请问他患病的概率是多少?

如果我们从大街上随便找一个人,那么他患病的概率就是0.1%,因为这个概率是基于历史统计数据的先验概率。

现在,他做了一次测试,结果为阳性,我们要计算他患病的概率,就是计算条件概率,即:在测试为阳性这一条件下,患病的概率是多少。

从直觉上这个人患病的概率大于0.1%,但也肯定小于99%。究竟是多少,怎么计算,我们先放一放。

为了理解条件概率,我们换一个更简单的例子:掷两次骰子,一共可能出现的结果有6×6=36种:

sample space

这就是所谓的样本空间,每个样本的概率均为1/36,这个很好理解。

如果我们定义事件A为:至少有一个骰子是2,那么事件A的样本空间如下图红色部分所示:

Event A

事件A一共有11种情况,我们计算事件A的概率P(A):

P(A)

我们再定义事件B:两个骰子之和为7,那么事件B的样本空间如下图绿色部分所示:

Event B

事件B一共有6种情况,我们计算事件B的概率P(B):

P(B)

接下来我们用P(A∩B)表示A和B同时发生的概率,A∩B就是A和B的交集,如下图蓝色部分所示:

P(A∩B)

显然A∩B只有两种情况,因此,计算P(A∩B):

P(A∩B)

接下来我们就可以讨论条件概率了。我们用P(A|B)表示在B发生的条件下,A发生的概率。由于B已经发生,所以,样本空间就是B的样本数量6,而要发生A则只能是A、B同时发生,即A∩B,有两种情况。

因此,计算P(A|B)如下:

P(A|B)

同理,我们用P(B|A)表示在A发生的条件下,B发生的概率。此时,分子仍然是A∩B的样本数量,但分母变成A的样本数量:

P(B|A)

可见,条件概率P(A|B)和P(B|A)是不同的。

我们再回到A、B同时发生的概率,观察P(A∩B)可以改写为:

P(B|A)xP(A)

同理,P(A∩B)还可以改写为:

P(A|B)xP(B)

因此,根据上述两个等式,我们推导出下面的等式:

P(AB)=P(ABP(B)=P(BAP(A)

把左边的P(A∩B)去掉,我们得到等式:

P(ABP(B)=P(BAP(A)

最后,整理一下等式,我们推导出贝叶斯定理如下:

P(AB)=P(B)P(BAP(A)​

这就是著名的贝叶斯定理,它表示,当出现B时,如何计算A的概率。

很多时候,我们把A改写为H,把B改写为E

P(HE)=P(E)P(EHP(H)​

H表示Hypothesis(假设),E表示Evidence(证据),贝叶斯定理的意义就在于,给定一个先验概率P(H),在出现了证据E的情况下,计算后验概率P(H|E)。

计算

有了贝叶斯定理,我们就可以回到开头的问题:

已知有一种疾病,发病率是0.1%。针对这种疾病的测试非常准确:

  • 如果有病,则准确率是99%(即有1%未检出阳性);
  • 如果没有病,则误报率是2%(即有2%误报为阳性)。

现在,如果一个人测试显示阳性,请问他患病的概率是多少?

用H表示患病,E表示测试为阳性,那么,我们要计算在测试为阳性的条件下,一个人患病的概率,就是计算P(H|E)。根据贝叶斯定理,计算如下:

P(HE)=P(E)P(EHP(H)​

P(H)表示患病的概率,根据发病率可知,P(H)=0.1%;

P(E|H)表示在患病的情况下,测试为阳性的概率,根据“如果有病,则准确率是99%”可知,P(E|H)=99%;

P(E)表示测试为阳性的概率。这个概率就稍微复杂点,因为它是指对所有人(包含病人和健康人)进行测试,结果阳性的概率。

我们可以把检测人数放大,例如放大到10万人,对10万人进行检测,根据发病率可知:

  • 有100人是病人,另外99900是健康人;
  • 对100个病人进行测试,有99人显示阳性,另有1人未检出(阴性);
  • 对99900个健康人进行测试,有2%=1998人显示阳性(误报),另有98%=97902人为阴性。

下图显示了检测为阳性的结果的分布:

           ┌───────┐
           │100000 │
           └───────┘
               │
       ┌───────┴───────┐
       ▼               ▼
   ┌───────┐       ┌───────┐
   │  100  │       │ 99900 │
   └───────┘       └───────┘
       │               │
   ┌───┴───┐       ┌───┴───┐
   ▼       ▼       ▼       ▼
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ 99  │ │  1  │ │1998 │ │97902│
└─────┘ └─────┘ └─────┘ └─────┘
   │               │
   ▼               ▼
   +               +

所以,对于10万人的样本空间来说,事件E=显示阳性的概率为(99+1998)/100000=2.097%。

带入贝叶斯定理,计算P(H|E):

P(HE)=P(E)P(EHP(H)​=2.097%99%×0.1%​=0.020970.99×0.001​=0.04721=4.721%

计算结果为患病的概率为4.721%,这个概率远小于99%,且与大多数人的直觉不同,原因在于庞大的健康人群导致的误报数量远多于病人,当出现“检测阳性”的证据时,患病的概率从先验概率0.1%提升到4.721%,还远不足以确诊。

贝叶斯定理的另一种表示

在上述计算中,我们发现计算P(E)是比较困难的,很多时候,甚至无法知道P(E)。此时,我们需要贝叶斯定理的另一种表示形式。

我们用P(H)表示H发生的概率,用H表示H不发生,P(H)表示H不发生的概率。显然P(H)=1-P(H)。

下图红色部分表示H,红色部分以外则表示H:

P(H)

事件E用绿色表示:

P(E)

可见,P(E)可以分为两部分,一部分是E和H的交集,另一部分是E和H的交集:

P(E)=P(EH)+P(EH)

根据上文的公式P(A∩B)=P(A|B)xP(B),代入可得:

P(E)=P(EH)+P(EH)=P(EHP(H)+P(EHP(H)

把P(E)替换掉,我们得到贝叶斯定理的另一种写法:

P(HE)=P(EHP(H)+P(EHP(H)P(EHP(H)​

用这个公式来计算,我们就不必计算P(E)了。再次回到开头的问题:

已知有一种疾病,发病率是0.1%。针对这种疾病的测试非常准确:

  • 如果有病,则准确率是99%(即有1%未检出阳性);
  • 如果没有病,则误报率是2%(即有2%误报为阳性)。

现在,如果一个人测试显示阳性,请问他患病的概率是多少?

  • P(E|H)表示患病时检测阳性的概率=99%;
  • P(H)表示患病的概率=0.1%;
  • P(E|H)表示没有患病但检测阳性的概率=2%;
  • P(H)表示没有患病的概率=1-P(H)=99.9%。

代入公式,计算:

P(HE)=P(EHP(H)+P(EHP(H)P(EHP(H)​=99%×0.1%+2%×99.9%99%×0.1%​=0.04721=4.721%

检测为阳性这一证据使得患病的概率从0.1%提升到4.721%。假设这个人又做了一次检测,结果仍然是阳性,那么他患病的概率是多少?

我们仍然使用贝叶斯定理计算,只不过现在先验概率P(H)不再是0.1%,而是4.721%,P(E|H)和P(E|H)仍保持不变,计算新的P(H|E):

P(HE)=P(EHP(H)+P(EHP(H)P(EHP(H)​=99%×4.721%+2%×(1−4.721%)99%×4.721%​=0.71=71%

结果为71%,两次检测为阳性的结果使得先验概率从0.1%提升到4.721%再提升到71%,继续第三次检测如果为阳性则概率将提升至99.18%。

可见,贝叶斯定理的核心思想就是不断根据新的证据,将先验概率调整为后验概率,使之更接近客观事实。

关于信念

我们再回顾一下贝叶斯定理:

P(HE)=P(E)P(EHP(H)​

稍微改一下,变为:

P(HE)=P(HP(E)P(EH)​

P(H)是先验概率,P(H|E)是后验概率,P(E|H)/P(E)被称为调整因子,先验概率乘以调整因子就得到后验概率。

我们发现,如果P(H)=0,则P(H|E)=0;如果P(H)=1,则P(E|H)=P(E),P(H|E)=1。

也就是说,如果先验概率为0%或100%,那么,无论出现任何证据E,都无法改变后验概率P(H|E)。这对我们看待世界的认知有重大指导意义,因为贝叶斯概率的本质是信念,通过一次次事件,我们可能加强某种信念,也可能减弱某种信念,但如果信念保持100%或0%,则可以做到对外界输入完全“免疫”。

举个例子,十年前许多人都认为比特币是庞氏骗局,如果100%坚定地持有这种信念,那么他将无视用户越来越多、价格上涨、交易量扩大、机构入市等诸多证据,至今仍然会坚信比特币是骗局而错过无数次机会。(注:此处示例不构成任何投资建议)

对于新生事物,每个人都可以有非常主观的先验概率,但只要我们不把先验概率定死为0或100%,就有机会改变自己的信念,从而更有可能接近客观事实,这也是贝叶斯定理的精髓:

你相信什么并不重要,重要的是你别完全相信它。

推荐个alist网盘聚合神器

https://github.com/alist-org/alist

有windows和liunx版本,可加入多种网盘,还可配合aria2等来离线下载。

如果要让别人查看的话在后台里把来宾帐户启用,再把无需密码访问选上。

安装后可以更改去掉相关特征信息

在设置-全局-自定义头部

<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<style>
.footer {
    display: none!important;
}
body:before {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: .3;
    z-index: -1;
    display: block;
    position: fixed;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center center;
    background-attachment: fixed;
    content: '';
    background-image: url(https://oss.iymark.com/2022/09/vback.jpg);
}
.hope-c-PJLV-ikSuVsl-css {
  background: none!important;
}
.markdown-body a {
  color: #000!important;
}
.hope-ui-dark .markdown-body a {
  color: #fff!important;
}
.copyright .link {
    padding: 4px;
    background: #26517c;
    border-radius: 0 8px 8px 0;
}
.copyright .name {
    padding: 4px;
    background: #fff;
    border-radius: 8px 0 0 8px;
    color:#000;
}
.copyright {
    padding: 50px;
    background: #2d3236!important;
}
.copyright a {
  color: #fff;
}
br.phone, br.pad{
    display:none;
}
@media (max-width: 891px){
br.pad {
    display:block;
}
}
@media (max-width: 561px){
br.phone {
    display:block;
}
}
.hope-c-PJLV-ieESZju-css {
    display: none;
}
img.hope-c-PJLV-ibwASZs-css {
    width: auto;
}
.hope-ui-dark .copyright .name {
    background: #000;
}
.runtime {
    margin-top: 20px;
    color: #fff;
    text-align: right!important;
}
.about, .state {
    width: min(99%, 980px);
    text-align: center;
    padding-inline: 2%;
}
.state {
    margin-top: 20px;
    color:#fff;
}
</style>

在自己定义内容里

<div class="copyright" align="center"><div class="about"><p>
<span class="name">© 2020-2023</span><span class="link"><a href="https://www.xtaa.cn">奇言</a></span><br class="phone"><br class="phone">
</p>
<div class="runtime">
<span id="runtime_span"></span>
<script type="text/javascript">
    function show_runtime() {
        window.setTimeout("show_runtime()", 1000);
        X = new Date("2/24/2023 00:00:00");
        Y = new Date();
        T = (Y.getTime() - X.getTime());
        M = 24 * 60 * 60 * 1000;
        a = T / M;
        A = Math.floor(a);
        b = (a - A) * 24;
        B = Math.floor(b);
        c = (b - B) * 60;
        C = Math.floor((b - B) * 60);
        D = Math.floor((c - C) * 60);
        runtime_span.innerHTML = "<span class=\"name\">稳定运行" + A + "天</span><span class=\"link\">" + B + "时" + C + "分" + D + "秒</span>"
    }
    show_runtime();
</script>
</div>
</div>
<div class="state"><p>免责声明:本站为个人网盘,网盘所发布的一切影视、源代码、注册信息及软件等资源仅限用于学习和研究目的</p></div>
</div>

android平台googleplay环境

最近有很多朋友来咨询我如何下载谷歌验证器。安卓手机下载谷歌验证器需要通过Google Play,而小米、华为等安卓手机因为手机供应商提供的版本不同,会限制,手机无法运行Google应用程序。这篇文章以小米手机为例,教大家如何在安卓手机上运行Google Play。

步骤如下:1、打开应用商店,搜higoplay安装器(如果要收费用后面的),如果没有则

搜索“豌豆荚”,下载“豌豆荚”App

在豌豆荚APP中搜索“谷歌安装器”,找到“SU谷歌安装器”(也可能更名为SuPlay安装器),或者到极光下载站等百度网站搜索也可以下,点击下载-安装

分别下载和安装对应的谷歌服务:谷歌服务框架、谷歌play服务、谷歌商店5、安装完成,登陆您的Google账户,即可使用Google Play功能。

激光窃听声音

现代激光窃听的基本原理,都是通过红外激光器,如:激光雷达,通过窗户向室内发射看不见的红外激光,激光透过玻璃窗到达室内物体时产生回波,这个回波是由于声音振动而引起声源周边物体产生同步的振动波,这个回波再通过窗户射出传递给接收器,由接收器通过准确的测量,并进行多普勒转换,所获数据经计算机进行处理,还原出音频。

      可以通过在窗户上贴宽光谱激光窃听阻断膜来阻止这类窃听

腾讯文档多人协作

要多人共同同时操作一个表或者文档时,可以用腾讯文档,石墨文档等,如下面说下腾讯文档

进腾讯文档后,可新建在线文档,在线EXCEL表,在线PPT,在线信息收集表,在线思维图等,创建人有所有权,

如创建在线表格后,可以通过右上解的分享等功能,或者其它人可扫微信等来得到查看和编辑权限,

有编辑权限后就可以一起操作表格了,如可以进行筛选等表格常用操作,可以用数据验证来设置单元格的格式为下位选框,复选框,日期等,

可以套用模板,用插件等

总体比较好用,别人在编辑一个单元格时也会有标示,以免两个人同时编辑一个单元格,但偶尔会出现别人编辑完,你这边还没显示的情况,

excel技能

同一个文档用函数时可以从一个表引用另一个表的数据,一般是这样子的 Sheet1!

比如这是从其它表中统计表Sheet1中G列中值为电信的个数=COUNTIF(Sheet1!G:G,”电信”)

excel数据透视表:比函数更强大方便的数据透视表,

在表中选定要进行透视操作的数据(可以是列),然后选 插入-数据透视表 来创建数据透视表,在透视表中可以在右边把相关的字段(原表的列)拖到下面的筛选,列,行,值中做出不同要求的透视表,一般筛选表示可以根据筛选项来只显示某一部分数据,把不同字段做行和列来构建不同的表,把某字段放到值里,来做为值来计数,(还可以左键直接点被拖入 值 这个框里的字段,选 “值字段设置”,可以从计数改为求和,求平均值等)(做出透视表后,直接点表中相关的数值就可以看到相关的原始表的内容)

excel函数vlookup,SUBSTITUTE,IF,SUMIF,COUNTIF,COUNTIFS

VLOOKUP函数的语法为:
VLOOKUP(要找谁,在哪儿找,返回第几列的内容,精确找还是近似找)

第一参数是要在表格或区域的第一列中查询的值。
第二参数是需要查询的单元格区域,这个区域中的首列必须要包含查询值,否则公式将返回错误值。如果查询区域中包含多个符合条件的查询值,VLOOKUP函数只能返回第一个查找到的结果。
第三参数用于指定返回查询区域中第几列的值,该参数如果超出待查询区域的总列数,VLOOKUP函数将返回错误值#REF!,如果小于1返回错误值#VALUE!。
第四参数决定函数的查找方式,如果为0或FASLE,用精确匹配方式,而且支持无序查找;如果为TRUE或被省略,则使用近似匹配方式,同时要求查询区域的首列按升序排序。

如图,需要从B~E的数据表中,根据H3单元格的姓名查询对应的职务。
公式为:
=VLOOKUP(H3,C:E,3,0)

A	B	C	D	E	F	G	H	I
	序号	姓名	部门	职务				
	1	红红	采购	经理			姓名	职务
	2	贺回	安监	保管			方方	经理
	3	关关	采购	部长				
	4	刘刘	安监	科长				
	5	方方	采购	经理				
	6	是有明	安监	代表				
	7	朱朱	采购	经理				
	8	以以	安监	保管				
	9	林林	采购	出纳				

在I3处插入以上函数,意思为在C到E列中精彩确查找结果和H3一样的内容,然后把相应内容的第3列的(内容C到E列中的第3列,VLOOKUP函数第三参数中的列号,不能理解为工作表中实际的列号,而是指定要返回查询区域中第几列的值。)相应内容填入I3

SUBSTITUTE函数的基础语法是:
SUBSTITUTE(要替换的文本,旧文本,新文本,[替换第几个])
最后一个参数,[替换第几个], 是可以省略的。

可以用来去掉空格,

如以下可以把B2列从4位到9位中的数字用星号代替=SUBSTITUTE(B2,MID(B2,4,5),”*****”)

IF函数

=IF(判断条件,符合条件时返回的值,不符合条件时返回的值)

示例:根据身份证号码提取性别

公式:=IF(MOD(MID(B3,17,1),2)=1,”男”,”女”)

身份证号码中也包含着每个人的性别信息,它只与身份证号码的第17位有关,第17位是计数性别为男,为偶数性别则为女,所以我们使用mid提取第17位的数值,然后使用mod函数计算这个数的奇偶性,最后使用if函数判断输出结果

sum 指定区域求和 统计一个单元格区域:=sum(A1:A10) 统计多个单元格区域:=sum(A1:A10,C1:C10)

SUMIF,用来对符合指定条件的数据求和。

=SUMIF(条件区域,指定的求和条件,求和的区域)

如要在B列中查找值和E2内容相同的相应多个C列的数值做求和 =SUMIF(B:B,E2,C:C)

COUNTIF,用来统计符合指定条件的个数

=COUNTIF(条件区域,指定的条件)

如要在B列中查找内容和E2相同的个数=COUNTIF(B:B,E2)

示例:查找标记重复内容

公式:=IF(COUNTIF(C:C,C3)>1,”重复”,””)

在这里我们首先使用countif函数对C列进行计数,如果结果大于1代表有重复值,然后我们使用if函数来判断结果是不是大于1,大于1就返回重复,如果不大于1就返回空值

COUNTIFS,用来统计符合指定多个条件的个数

=COUNTIFS(条件区域1,指定的条件1,条件区域2,指定的条件2,条件区域3,指定的条件,…………..)

比如要查下A列中值等于电信而且B列中值等于E2值的个数=COUNTIF(A:A,”电信”B:B,E2)

SUMPRODUCT函数是在给定的几组数组中,将数组间对应的元素相乘,并返回乘积之和。SUMPRODUCT函数完整语法如下:

=SUMPRODUCT(array1,[array2], [array3], …

Array1:必需。其相应元素需要进行相乘并求和的第一个数组参数。

Array2, array3,…:可选。 2 到 255 个数组参数,其相应元素需要进行相乘并求和。

第一种使用场景:快速的相乘相加

ABCDEF
水果苹果香蕉提子荔枝总价
单价86710总价
20210405销量12013517888
20210406销量13312215798
20210407销量15211313776

在F3列填=SUMPRODUCT(B2:E2,B3:E3) 就可算出今天这几个水果的总收了,具体就是各水果的单价分别乘相应销量再加一起

第二种使用场景:条件求和

ABCDE
水果2月5号销售额
苹果25
香蕉26水果总销售额
荔枝36苹果
苹果63
香蕉27
苹果52

在E4中插入公式

=SUMPRODUCT((A2:A7=”苹果”)*B2:B7)就可以得出总个苹果的总销售额,就是B2+B5+B7

注意:这里用乘号相连是因为A2:A10=”苹果”,是一个逻辑判断,返回的是逻辑值,而后面的是数字,所以需要用乘号。

max 指定区域最大值,数据计算