貝葉斯AB測試

AB測試是用來評估變更效果的有效方法,但很多時候會運行大量AB測試,如果能夠在測試中復用之前測試的結果,將有效提升AB測試的效率和有效性。原文: Bayesian AB Testing[1]

alt

隨機實驗,又稱AB測試,是行業中評估因果效應的既定標準。將新方法(新產品、功能、UI等)隨機分配給人群中的特定子集(用戶、患者、客戶等),從而確保平均來說,結果的差異(收入、訪問量、點擊量等)可以歸因于不同的方法。像Booking.com[2]這樣的老牌公司報告說,他們會同時運行數千個AB測試。而多鄰國(Duolingo)[3]等新興公司的成功很大程度上要歸功于他們的大規模實驗文化。

做了這么多實驗,自然而然出現了一個問題: 在某個具體實驗中,能不能利用以前的測試信息?如何利用?在這篇文章中,我們將嘗試通過介紹AB測試的貝葉斯方法來回答這些問題。貝葉斯框架很適合這種類型的任務,允許使用新數據更新現有(先驗)知識。然而,該方法對函數形式假設特別敏感,模型選擇(如先驗分布的偏度)的微小區別可以造成非常不同的估算結果。


搜索和無限滾動

在本文其余部分,我們將使用一個玩具示例,該示例受到Azavedo等人(2019)[4]的啟發: 搜索引擎希望在不犧牲搜索質量的情況下增加廣告收入。我們是一家擁有成熟實驗文化的公司,不斷測試如何改進登錄頁面的新想法。假設我們想出了一個絕妙的新想法: 無限滾動[5]!如果用戶想看到更多結果,他們可以繼續向下滾動,而不是顯示離散的頁面序列。

alt

我們通過AB測試了解無限滾動是否有效: 我們將用戶隨機分為測試組和對照組,只對測試組用戶實施無限滾動。我們從```src.dgp```[6]導入數據,生成dgp_infinite_scroll()。對于以前的文章,我們生成了新的DGP父類,處理隨機化和數據生成,其子類包含了具體用例。我們還從```src.utils```[7]中導入了繪圖函數和庫。為了不僅包含代碼,還包括數據和表格,我們使用Deepnote[8],一個類似于Jupyter的網絡協作筆記本環境。

from?src.utils?import?*
from?src.dgp?import?DGP,?dgp_infinite_scroll

dgp?=?dgp_infinite_scroll(n=10_000)
df?=?dgp.generate_data(true_effect=0.14)
df.head()
past_revenueinfinite_scrollad_revenue
03.7613.70
12.4011.71
22.9814.85
34.2414.57
43.8703.69

我們有1萬名網站訪問者信息,觀察他們每月產生的ad_revenue,考慮是否被分配到測試組并使用infinite_scroll,以及每月平均past_revenue

隨機測試分配使得均數差(difference-in-means) 估算器沒有偏差[9]。我們期望測試組和對照組平均來看具有可比性,因此可以將觀察到的平均結果差異歸因于測試效果,然后用線性回歸估計測試效果,從而可以將測試效果解釋為infinite_scroll的作用。

smf.ols('ad_revenue ~ infinite_scroll', df).fit().summary().tables[1]
alt

看起來infinite_scroll確實是個好主意,增加了0.1524美元的月平均收益。此外,在1%的置信水平下,該效應顯著高于零。

我們可以通過在回歸中控制past_revenue來進一步提高估算器精度。我們不期望估算系數有明顯變化,但精度應該會提高(如果想了解更多關于控制變量的信息,請查看關于CUPED[10]DAG[11]的其他文章)。

reg?=?smf.ols('ad_revenue?~?infinite_scroll?+?past_revenue',?df).fit()
reg.summary().tables[1]
alt

事實上,past_revenue可以準確預測當前ad_revenue,而infinite_scroll估算系數的精度降低了三分之一。

到目前為止,一切都很正常。然而,正如開頭所說,假設這不是我們為改進瀏覽器(并最終提高廣告收入)而進行的唯一實驗,無限滾動只是我們過去測試過的數千個想法中的一個,有沒有一種方法可以有效利用這些額外信息?


貝葉斯統計

貝葉斯統計相對于頻率論方法(frequentist approach)的主要優勢之一是可以比較容易的將額外信息合并到模型中,該想法來源于貝葉斯統計背后的貝葉斯定理(Bayes Theorem)[12],貝葉斯定理允許我們通過反轉推理問題對模型進行推理: 從給定數據的模型的概率,到給定模型的數據的概率,從而使該對象更容易被處理。

貝葉斯定理
貝葉斯定理

可以把貝葉斯定理的右邊分成兩個部分: 先驗(prior)可能性(likelihood) ,可能性來自數據關于模型的信息,先驗則是關于模型的任何附加信息。

首先,我們把貝葉斯定理映射到環境中,明確數據是什么、模型是什么、我們感興趣的對象是什么。

  • 數據(data) 包括結果變量 ad_revenue(表示為 y),測試變更 infinite_scroll(表示為 D和其他變量), past_revenue和常量共同表示為 X
  • 模型(model) 是在給定 past_revenueinfinite_scroll特性的條件下, ad_revenue的分布,表示為 y|D,X
  • 感興趣的對象是得到的 Pr(model|data) ,特別是 ad_revenueinfinite_scroll之間的關系
X?=?sm.add_constant(df[['past_revenue']].values)
D?=?df['infinite_scroll'].values
y?=?df['ad_revenue'].values

如何在AB測試上下文中使用可能包含了額外協變量的先驗信息?

貝葉斯回歸

我們用線性模型來直接與頻率論方法進行比較:

條件分布y|x
條件分布y|x

這是一個參數模型,有兩組參數: 線性系數β和τ,以及殘差方差σ。等價但更符合貝葉斯模型的寫法是:

條件分布y|x
條件分布y|x

其中半列將數據與模型參數分開。與頻率論方法不同,在貝葉斯回歸中,不依賴中心極限定理[13]來近似y的條件分布,而是直接假設它是正態分布。

我們感興趣的是對模型參數β、τ和σ進行推理。頻率方法和貝葉斯方法的另一個核心區別是,前者假設模型參數是固定、未知的,而后者允許參數是隨機變量。

這個假設有非常實際的含義: 可以很容易的以先驗分布的形式合并關于模型參數的先驗信息。顧名思義,先驗包含了查看數據之前的可用信息。這就引出了貝葉斯統計中最重要的一個相關問題: 如何選擇先驗信息?

先驗信息

選擇先驗信息時,一個有吸引力的限制是確定先驗分布,使得后驗信息屬于同一家族,這叫做共軛先驗(conjugate priors) 。例如,在看到數據之前,假設測試效果是正態分布的,在結合數據中包含的信息后,我們希望它也是正態分布的。

在貝葉斯線性回歸的情況下,β、τ和σ的共軛先驗是正態分布和逆伽瑪分布,我們選擇從標準正態和逆伽馬分布開始。

先驗分布
先驗分布

我們用概率編程包PyMC[14]進行推理。首先,需要指定模型: 不同參數的先驗分布和數據的可能性。

import?pymc?as?pm
with?pm.Model()?as?baseline_model:

????#?Priors
????beta?=?pm.MvNormal('beta',?mu=np.ones(2),?cov=np.eye(2))
????tau?=?pm.Normal('tau',?mu=0,?sigma=1)
????sigma?=?pm.InverseGamma('sigma',?mu=1,?sigma=1,?initval=1)
????
????#?Likelihood?
????Ylikelihood?=?pm.Normal('y',?mu=(X@beta?+?D@tau).flatten(),?sigma=sigma,?observed=y)

PyMC有一個非常好的函數model_to_graphviz,允許我們將模型可視化為圖形。

pm.model_to_graphviz(baseline_model)
模型圖
模型圖

從圖中可以看到各種模型組件、分布,以及如何相互作用。

現在準備計算模型的后驗。我們對模型參數的實現進行抽樣,計算給定值的數據的可能性,并推導出相應的后驗。

idata?=?pm.sample(model=baseline_model,?draws=1000)

貝葉斯推理需要抽樣,這在歷史上一直是貝葉斯統計的主要瓶頸之一,因為它比頻率論方法要慢得多。然而,隨著計算機模型計算能力的增強,這已不再是問題。

現在準備檢查結果。首先,使用summary()方法,可以打印與用于線性回歸的```statmodels```[15]包生成的模型摘要非常相似的模型摘要。

pm.summary(idata,?hdi_prob=0.95).round(4)
meansdhdi_2.5%hdi_97.5%mcse_meanmcse_sdess_bulkess_tailr_hat
beta[0]0.0190.025-0.0310.0680.0010.01943.01866.01.0
beta[1]0.9920.0100.9701.0110.0000.02239.01721.01.0
tau0.1570.0210.1170.1970.0000.02770.02248.01.0
sigma0.9930.0070.9801.0070.0000.03473.02525.01.0

估算的參數與頻率論方法得到的參數非常接近,infinite_scroll的估算效果等于0.157。

如果取樣的缺點是速度慢,那么優點是非常透明,可以直接畫出后驗的分布。我們來計算一下測試效應τ,PyMC函數plot_posterior繪制后驗分布,黑色條表示貝葉斯等價的95%置信區間。

pm.plot_posterior(idata,?kind="hist",?var_names=('tau'),?hdi_prob=0.95,?figsize=(6,?3),?bins=30);?
τ的后驗分布
τ的后驗分布

和預期一樣,由于我們選擇了共軛先驗,后驗分布看起來是高斯分布。

目前為止,我們并沒有對選擇先驗施加太多指導。然而,假設我們可以查閱過去的實驗,如何整合這些特定信息?


過去的實驗

假設無限滾動的想法只是我們過去嘗試和測試過的眾多想法中的一個,對于每個想法,都有相應的實驗數據,以及相應的估算系數。

past_experiments?=?[dgp.generate_data(seed_data=i)?for?i?in?range(1000)]
taus?=?[smf.ols('ad_revenue?~?infinite_scroll?+?past_revenue',?pe).fit().params.values?for?pe?in?past_experiments]

我們從過去實驗中得出了1000個估算值,那如何使用這些額外的信息呢?

常態先驗

第一個想法可能是校準先驗,以反映過去的數據分布。我們維持正態假設,使用過去實驗估算的平均值和標準差。

taus_mean?=?np.mean(taus,?axis=0)[1]

taus_mean計算結果為0.0009094486420266667,意味著平均而言,對ad_revenue幾乎沒有影響,平均影響為0.0009。

taus_std?=?np.sqrt(np.cov(taus,?rowvar=0)[1,1])

taus_std計算結果為0.029014447772168384,意味著各實驗之間存在明顯的變化,標準偏差為0.029。

重寫模型,使用過去估算τ的先驗分布均值和標準差。

with?pm.Model()?as?model_normal_prior:

????#?Priors
????beta?=?pm.MvNormal('beta',?mu=np.ones(2),?cov=np.eye(2))
????tau?=?pm.Normal('tau',?mu=taus_mean,?sigma=taus_std)
????sigma?=?pm.InverseGamma('sigma',?mu=1,?sigma=1,?initval=1)

????#?Likelihood
????Ylikelihood?=?pm.Normal('y',?mu=(X@beta?+?D@tau).flatten(),?sigma=sigma,?observed=y)

從模型中取樣:

idata_normal_prior?=?pm.sample(model=model_normal_prior,?draws=1000)

并繪制參數τ的樣本后驗分布處理效果圖。

pm.plot_posterior(idata_normal_prior,?kind="hist",?var_names=('tau'),?hdi_prob=0.95,?figsize=(6,?3),?bins=30);?
τ的后驗分布
τ的后驗分布

估算系數明顯較小,為0.11,而不是先前估算的0.16。為什么會這樣呢?

事實是,考慮到我們的先驗,之前的系數0.16是極不可能的。在給定先驗條件下,可以計算得到相同或更極端值的概率。

1?-?sp.stats.norm(taus_mean,?taus_std).cdf(0.16)

計算結果為2.0532795019789774e-08,概率幾乎為零。因此,估算系數已經向先前的平均值0.0009移動。

T先驗(Student-t Prior)

目前為止,我們假設所有線性系數都是正態分布。這樣合適嗎?讓我們從截距系數(intercept coefficient) β 開始直觀檢查。

sns.histplot([tau[0]?for?tau?in?taus]).set(title=r'Distribution?of?$\hat{\beta}_0$?in?past?experiments');
alt

分布似乎很正常,那么效果參數τ呢?

fig,?ax?=?plt.subplots()
sns.histplot([tau[1]?for?tau?in?taus],?label='past?experiments');
ax.axvline(reg.params['infinite_scroll'],?lw=2,?c='C3',?ls='--',?label='current?experiment')
plt.legend();
plt.title(r'Distribution?of?$\hat{\tau}$?in?past?experiments');
alt

這是一個非常肥尾(heavy-tailed) 的分布,在中心看起來像正態分布,尾部更"胖",有兩個非常極端的值。排除測量誤差,這是行業中經常發生的情況,大多數想法的影響都非常小或為零,很少有想法是突破性的。

模擬這種分布的一種方法是T分布(student-t)[16]。我們用均值為0.0009,方差為0.003,自由度為1.3的T分布來匹配過去估算的經驗分布矩陣。

with?pm.Model()?as?model_studentt_prior:

????#?Priors
????beta?=?pm.MvNormal('beta',?mu=np.ones(2),?cov=np.eye(2))
????tau?=?pm.StudentT('tau',?mu=taus_mean,?sigma=0.003,?nu=1.3)
????sigma?=?pm.InverseGamma('sigma',?mu=1,?sigma=1,?initval=1)
????
????#?Likelihood?
????Ylikelihood?=?pm.Normal('y',?mu=(X@beta?+?D@tau).flatten(),?sigma=sigma,?observed=y)

從模型中取樣。

idata_studentt_priors?=?pm.sample(model=model_studentt_prior,?draws=1000)

并繪制參數τ的樣本后驗分布處理效果圖。

pm.plot_posterior(idata_studentt_priors,?kind="hist",?var_names=('tau'),?hdi_prob=0.95,?figsize=(6,?3),?bins=30);?
τ的后驗分布
τ的后驗分布

估算系數類似于用標準先驗得到的系數0.11,然而由于置信區間從[0.077,0.016]縮小到[0.065,0.015],估算更加精確。

發生了什么?

收縮

答案在于所用的不同先驗分布形態:

  • 標準正態,N(0,1)
  • 正態矩匹配,N(0,0.03)
  • T型匹配矩陣, (0, 0.003)
t_hats?=?np.linspace(-0.3,?0.3,?1_000)
distributions?=?{
????'N(0,1)':?sp.stats.norm(0,?1).pdf(t_hats),
????'N(0,?0.03)':?sp.stats.norm(0,?0.03).pdf(t_hats),
????'$t_{1.3}$(0,?0.003)':?sp.stats.t(df=1.3).pdf(t_hats?/?0.003)*300,
}

畫在一起看看。

for?i,?(label,?y)?in?enumerate(distributions.items()):
????sns.lineplot(x=t_hats,?y=y,?color=f'C{i}',?label=label);
plt.xlim(-0.15,?0.15);
plt.legend();?
plt.title('Prior?Distributions');
不同的先驗分布
不同的先驗分布

正如我們所看到的,所有分布都以0為中心,但形狀非常不同。標準正態分布在[-0.15,0.15]區間內基本上是平坦的,每個值的概率基本相同。而后兩個盡管有相同的均值和方差,但形態非常不同。

這如何轉化為我們的估算?對每個先驗分布,可以畫出不同估算的隱含后驗。

fig,?ax?=?plt.subplots(figsize=(7,6))
ax.axvline(reg.params['infinite_scroll'],?lw=2,?ls='--',?c='darkgray');
for?i,?(label,?y)?in?enumerate(distributions.items()):
????sns.lineplot(x=t_hats,?y=[compute_posterior(t,?y)?for?t?in?t_hats]?,?color=f'C{i}',?label=label);
plt.legend();?
ax.set_xlabel('Experiment?Estimate');
ax.set_ylabel('Posterior');
先驗對實驗估算的影響
先驗對實驗估算的影響

正如我們所看到的,不同的先驗以非常不同的方式改變實驗估算。標準正態先驗基本上對[-0.15,0.15]區間內的估算沒有影響。具有匹配矩陣的正常先驗反而使每個估算值縮小約2/3。T先驗的影響是非線性的: 將小估算縮小到零,而保持大估算不變。灰色虛線標記了不同先驗對實驗估算τ的影響。

alt

結論

通過本文,我們了解了如何擴展AB測試的分析,以合并來自過去實驗的信息。我們特別介紹了AB測試的貝葉斯方法,看到選擇先驗分布的重要性。在相同均值和方差下,假設存在"肥尾"(非常偏斜)的先驗分布,意味著小效應的收縮更強,而大效應的收縮更少。

直覺上,帶有"肥尾"的先驗分布相當于假設突破性想法是罕見的,但不是不可能。正如我們在這篇文章中所看到的,這在實驗后有實際意義,但在實驗前也有意義。事實上,正如Azevedo等人(2020)[17]所報告的那樣,如果你認為想法的效果分布比較"正常",那么最好是進行少量但大型的實驗,以便能夠發現較小的效果。相反,如果你認為想法是"要么是突破性的,要么毫無意義",即效果是肥尾的,那么運行小而多的實驗更有意義,因為不需要大規模實驗來檢測大的效果。

代碼

本文所有代碼都在Jupyter Notebook上: https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/bayes_ab.ipynb。


你好,我是俞凡,在Motorola做過研發,現在在Mavenir做技術工作,對通信、網絡、后端架構、云原生、DevOps、CICD、區塊鏈、AI等技術始終保持著濃厚的興趣,平時喜歡閱讀、思考,相信持續學習、終身成長,歡迎一起交流學習。
微信公眾號:DeepNoMind

參考資料

[1]

Bayesian AB Testing: https://towardsdatascience.com/bayesian-ab-testing-ed45cc8c964d

[2]

The role of experimentation at Booking.com: https://partner.booking.com/en-gb/click-magazine/industry-perspectives/role-experimentation-bookingcom

[3]

Improving Duolingo, one experiment at a time: https://blog.duolingo.com/improving-duolingo-one-experiment-at-a-time

[4]

Empirical Bayes Estimation of Treatment Effects with Many A/B Tests: An Overview: https://www.aeaweb.org/articles?id=10.1257/pandp.20191003

[5]

Continuous scrolling comes to Search on mobile: https://blog.google/products/search/continuous-scrolling-mobile

[6]

src.dgp: https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/dgp.py

[7]

src.utils: https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/utils.py

[8]

Deepnote: https://deepnote.com

[9]

Bias of an estimator: https://en.wikipedia.org/wiki/Bias_of_an_estimator

[10]

Understanding CUPED: https://towardsdatascience.com/understanding-cuped-a822523641af

[11]

DAGs and Control Variables: https://towardsdatascience.com/controls-b63dc69e3d8c

[12]

貝葉斯定理(Bayes Theorem): https://en.wikipedia.org/wiki/Bayes'_theorem

[13]

中心極限定理: https://en.wikipedia.org/wiki/Central_limit_theorem

[14]

PyMC: https://www.pymc.io/projects/docs/en/stable/learn.html

[15]

statmodels: https://www.statsmodels.org/dev/index.html

[16]

T分布(student-t): https://en.wikipedia.org/wiki/Student%27s_t-distribution

[17]

A/B Testing with Fat Tails: https://www.journals.uchicago.edu/doi/full/10.1086/710607

- END -

本文由 mdnice 多平臺發布

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160093.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160093.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160093.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自定義類型:結構體

1.結構體類型的聲明 1.1結構體的概念 結構是?些值的集合,這些值稱為成員變量。結構的每個成員可以是不同類型的變量。 1.2 結構的聲明 struct tag {member-list; }variable-list; 例如描述?個學?: struct Stu {char name[20];//名字int age;//年…

【Mysql】[Err] 1293 - Incorrect table definition;

基本情況 SQL文件描述 /* Navicat MySQL Data TransferSource Server : cm4生產-200 Source Server Version : 50725 Source Host : 192.168.1.200:3306 Source Database : db_wmsTarget Server Type : MYSQL Target Server Version : 50725 File…

vxe編輯保存表格

業務需求: 1、需要點擊編輯時,全部表格顯示編輯框,點擊保存,全部保存。 2、因為位置問題,產品經理把24小時分成了兩行,開發就得分兩個表格。列標題是寫死的,文字偏移也是寫死的,其他…

服務器主機安全的重要性及防護策略

在數字化時代,服務器主機安全是任何組織都必須高度重視的問題。無論是大型企業還是小型企業,無論是政府機構還是個人用戶,都需要確保其服務器主機的安全,以防止數據泄露、網絡攻擊和系統癱瘓等嚴重后果。 一、服務器主機安全的重…

__int128類型movaps指令crash

結論 在使用__int128時,如果__int128類型的內存起始地址不是按16字節對齊的話,有些匯編指令會拋出SIGSEGV使程序crash。 malloc在64位系統中申請的內存地址,是按16字節對齊的,但一般使用時經常會申請一塊內存自己切割使用&#…

Qt的一個無邊界窗口公共類

頭文件&#xff1a; #pragma once #include <QWidget>class CFrameLessWidgetBase :public QWidget { public:CFrameLessWidgetBase(QWidget* p nullptr);~CFrameLessWidgetBase() {}protected:bool nativeEvent(const QByteArray& eventType, void* message, long…

static和extern

1.extern extern 是?來聲明外部符號的&#xff0c;如果?個全局的符號在A?件中定義的&#xff0c;在B?件中想使?&#xff0c;就可以使? extern 進?聲明&#xff0c;然后使?。 即在一個源文件中想要使用另一個源文件&#xff0c;即可通過這個extern來聲明使用。 2.st…

未來制造業的新引擎:工業機器人控制解決方案

制造業正經歷著一場革命性的變革 在這個變革的浪潮中&#xff0c;工業機器人成為推動制造業高效生產的關鍵力量。然而&#xff0c;要發揮機器人的最大潛力&#xff0c;一個強大而智能的控制系統是必不可少的。在這個領域&#xff0c;新一代的工業機器人控制解決方案正嶄露頭角&…

學習MySQL先有全局觀,細說其發展歷程及特點

學習MySQL先有全局觀&#xff0c;細說其發展歷程及特點 一、枝繁葉茂的MySQL家族1. 發展歷程2. 分支版本 二、特點分析1. 常用數據庫2. 選型角度及場景 三、三大組成部分四、總結 相信很多同學在接觸編程之初&#xff0c;就接觸過數據庫&#xff0c;而對于其中關系型數據庫中的…

這樣寫postman實現參數化,阿里p8都直呼牛逼

什么時候會用到參數化 比如&#xff1a;一個模塊要用多組不同數據進行測試 驗證業務的正確性 Login模塊&#xff1a;正確的用戶名&#xff0c;密碼 成功&#xff1b;錯誤的用戶名&#xff0c;正確的密碼 失敗 postman實現參數化 在實際的接口測試中&#xff0c;部分參數…

你的關聯申請已發起,請等待企業微信的管理員確認你的申請

微信支付對接時&#xff0c;需要申請AppID,具體在下面的位置&#xff1a; 關聯AppID&#xff0c;發起申請時&#xff0c;會提示這么一句話&#xff1a; 此時需要登錄企業微信網頁版&#xff0c;使用注冊人的企業微信掃碼登錄進去&#xff0c;然后按照下面的步驟操作即可。 點擊…

iEnglish全國ETP大賽:教育游戲助力英語習得

“seesaw,abacus,sword,feather,frog,lion,mouse……”11月18日,經過3局的激烈較量,“以過客之名隊”的胡玲、黃長翔、林家慷率先晉級“玩轉英語,用iEnglish”第三屆全國ETP大賽的16強,在過去的周末中,還有TIK徘徊者隊、不負昭華隊、溫柔殺戮者隊先后晉級。據悉,根據活動規則,在…

電腦內存升級

ddr代兼容 自從DDR內存時代開啟之后&#xff0c;只要滿足內存的插槽規格相同(DDR3或DDR4或DDR5即為內存規格)這一條件&#xff0c;不同品牌、不同頻率以及不同容量的茶品都可以一起使用&#xff0c;除了品牌和容量的影響之外&#xff0c;不同頻率的搭配可能會造成性能方面的影…

面試官:什么是三色標記

程序員的公眾號&#xff1a;源1024&#xff0c;獲取更多資料&#xff0c;無加密無套路&#xff01; 最近整理了一波電子書籍資料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虛擬機》&#xff0c;《重構改善既有代碼設計》&#xff0c;《MySQL高性能-第3版》&…

git提交時會將target也提交

有時候大家在提交git時發現會將編譯文件target也提交上去&#xff0c;這種情況有以下幾種情況 情況1&#xff1a;項目沒有設置.gitignore 情況2&#xff1a;設置了.gitignore但是依然會提交。 第一種&#xff1a;添加.gitignore&#xff0c;并在文件中添加需要忽略的東西。 …

redis分布式鎖的學習記錄

核心性質 獨占性&#xff1a;對于同一把鎖&#xff0c;同一時刻只能被一個加鎖方獨占 健壯性&#xff1a;不能產生死鎖。如果有一個因為宕機無法主動解鎖&#xff0c;鎖也應該被正常加載 對稱性&#xff1a;加成和解鎖的使用方必須為同一個身份&#xff0c;不允許被非方釋放 高…

HCIA-實驗命令基礎學習:

視頻學習&#xff1a; 第一部分&#xff1a;基礎學習。 19——子網掩碼。 27——防火墻配置&#xff1a; 32——企業級路由器配置&#xff1a; 基礎實驗完成&#xff1a;&#xff08;完成以下目錄對應的實驗&#xff0c;第一部分基礎實驗就完成。&#xff09; 方法&#xff…

C //習題 8.13 寫一個用矩形法求定積分的通用函數,分別求

C程序設計 &#xff08;第四版&#xff09; 譚浩強 習題8.13 習題 8.13 寫一個用矩形法求定積分的通用函數&#xff0c;分別求 ∫ 0 1 s i n x d x &#xff0c; ∫ 0 1 c o s x d x &#xff0c; ∫ 0 1 e x d x \int_{0}^{1}sinx\ dx&#xff0c;\ \ \int_{0}^{1}cosx\ …

ILI9225 TFT顯示屏16位并口方式驅動

所用屏及資料如后圖&#xff1a; ILI9225&#xff0c;176*220&#xff0c;8位或16位并口屏&#xff0c;IM0接GND&#xff0c;電源及背光接3.3v 主控&#xff1a;CH32V307驅動&#xff08;庫文件和STM32基本一樣&#xff09; 一、源碼 ILI9225.c #include "ILI9225.h&quo…

設計模式(二)-創建者模式(4)-原型模式

一、為何需要原型模式&#xff08;Prototype Pattern&#xff09;? 在軟件設計中&#xff0c;我們會遇到到這樣的情況&#xff1a;對原對象進行拷貝一個新的副本。想要實現這樣的邏輯&#xff0c;有一種笨方法就是對原對象里的所有變量進行逐一賦值。但是這樣的做法會導致代碼…