用 C++ 的方式揭秘 2024 春晚魔術(shù)背后的秘密!
聊一聊
今年春晚,屏幕前的很多觀眾跟隨劉謙的撲克牌魔術(shù)一起見證了奇跡,同時(shí)也讓全國(guó)網(wǎng)友都知道了“小尼的撲克牌沒對(duì)上”!
后來我們知道了,原來小尼兩張牌是 Q 和 A。
或許這就是上天送給我們的巧合!
Q 跟 A 合在一起,世間萬事皆有答案。
正如我們的生活:一半是問題、一半是答案。
用 C++ 代碼模擬春晚魔術(shù)
1.概述
今天我用代碼的方式,給大家揭露春晚魔術(shù)背后的秘密。
僅用代碼模擬整個(gè)過程,不探討其數(shù)學(xué)原理。
2.先看結(jié)果
(1) 抽取 4 張牌
首先要隨機(jī)抽取 4 張牌,然后撕成兩邊,堆在一起:
(2) 按名字移動(dòng)牌
把名字長(zhǎng)度數(shù)量的牌放到底部:
(3) 移動(dòng)開頭 3 張牌
把開頭的 3 張牌插入剩下牌中間:
(4) 藏牌
把最上面的牌藏起來:
(5) 按地區(qū)移牌
根據(jù)地區(qū)把開頭對(duì)應(yīng)數(shù)量的牌插入剩下牌中間:
(6) 按性別扔牌
按性別扔掉開頭對(duì)應(yīng)數(shù)量的牌:
(7) 見證奇跡的時(shí)刻
依次把開頭的牌挪到末尾:
(8) 好運(yùn)留下來煩惱丟出去
把開頭的牌挪到末尾,然后扔一張牌,直到剩下一張:
(9) 結(jié)果對(duì)比
把剩下的牌和藏的牌比較:
3.關(guān)鍵知識(shí)點(diǎn)
(1) std::shuffle
std::shuffle 是 C++11 中引入的一個(gè)函數(shù),用于隨機(jī)排列容器中的元素,即洗牌。
(2) std::random_device
std::random_device 是 C++11 中引入的一個(gè)隨機(jī)數(shù)生成器,用于生成隨機(jī)數(shù)。
std::random_device 通常用于生成種子,然后用這些種子初始化其他隨機(jī)數(shù)生成器,如 std::mt19937。
(3) std::rotate
std::rotate 是 C++ 標(biāo)準(zhǔn)庫中的一個(gè)算法。
該算法用于旋轉(zhuǎn)容器中的元素,將指定元素移動(dòng)到容器的開頭,同時(shí)將其他元素按照原來的順序移動(dòng)。
4.完整代碼
void print_cards(const std::vector<std::string>& deck) {
for (auto& card : deck) {
std::cout << card<< " ";
}
std::cout << std::endl;
std::cout << std::endl;
}
int main() {
std::vector<std::string> cards = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };
std::cout << "初始撲克牌:" << std::endl ;
print_cards(cards);
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(cards.begin(), cards.end(), g);
std::cout << "洗牌:" << std::endl;
print_cards(cards);
std::vector<std::string> random_8_cards;
for (int i = 0; i < 4; ++i) {
const auto idx = rd() % cards.size();
random_8_cards.push_back(cards[idx]);
cards.erase(cards.begin() + idx);
}
std::cout << "隨機(jī)抽取 4 張牌:" << std::endl;
print_cards(random_8_cards);
for( int i = 0;i < 4;++i)
{
random_8_cards.push_back(random_8_cards[i]);
}
std::cout << "把牌撕成兩半后疊一塊:" << std::endl;
print_cards(random_8_cards);
// 根據(jù)名字字?jǐn)?shù)調(diào)整牌的順序
int name_length;
std::cout << "請(qǐng)輸入名字字?jǐn)?shù):";
std::cin >> name_length;
std::cout << "名字長(zhǎng)度為:" << name_length << std::endl;
std::rotate(random_8_cards.begin(), random_8_cards.begin() + name_length, random_8_cards.end());
std::cout << "將開頭 "<< name_length <<" 張牌放入底部" << std::endl;
print_cards(random_8_cards);
std::rotate(random_8_cards.begin(), random_8_cards.begin() + 3, random_8_cards.begin()+ std::uniform_int_distribution<>(4, static_cast<int>(random_8_cards.size()) - 2)(g));
std::cout << "把前三張牌隨機(jī)插入剩余牌中:" << std::endl;
print_cards(random_8_cards);
const std::string hidden_card = random_8_cards.front();
std::cout << "把最上面的牌為:" << hidden_card << std::endl;
random_8_cards.erase(random_8_cards.begin());
std::cout << "把最上面的牌藏起來:" << std::endl;
print_cards(random_8_cards);
int location;
std::cout << "請(qǐng)輸入地區(qū),南方人輸入1,北方人輸入2,無法確定輸入3:";
std::cin >> location;
std::cout << "地區(qū)為:" << location << std::endl;
std::rotate(random_8_cards.begin(), random_8_cards.begin() + location, random_8_cards.begin() + std::uniform_int_distribution<>(location+1, random_8_cards.size() - 2)(g));
std::cout << "將開頭 " << location << " 張牌隨機(jī)插入剩余牌中" << std::endl;
print_cards(random_8_cards);
int gender;
std::cout << "請(qǐng)輸入性別,男性輸入1,女性輸入2:";
std::cin >> gender;
std::cout << "性別為:" << gender << std::endl;
for (int i = 0; i < gender; ++i) {
random_8_cards.erase(random_8_cards.begin());
}
std::cout << " 扔掉開頭的 "<< gender <<" 張牌" << std::endl;
print_cards(random_8_cards);
const std::vector<std::string> temp = {"見","證","奇","跡","的","時(shí)","刻"};
for (const auto& i : temp)
{
std::rotate(random_8_cards.begin(), random_8_cards.begin() + 1, random_8_cards.end());
std::cout << i << std::endl;
print_cards(random_8_cards);
}
while (random_8_cards.size() > 1) {
random_8_cards.push_back(random_8_cards.front());
random_8_cards.erase(random_8_cards.begin());
std::cout << "好運(yùn)留下來" << std::endl;
print_cards(random_8_cards);
random_8_cards.erase(random_8_cards.begin());
std::cout << "煩惱丟出去" << std::endl;
print_cards(random_8_cards);
}
std::cout << "剩余最后一張牌是:" << random_8_cards.front() << std::endl;
std::cout << "藏起來的一張牌是:" << hidden_card << std::endl;
return 0;
}
5.結(jié)尾
用 C++ 來實(shí)現(xiàn)還是很方便的,可能比用 Python 稍微多幾行代碼吧。
所以,你看出來小尼老師是哪一步做錯(cuò)了嗎?
其實(shí),在這一步已經(jīng)確定了,只要開頭和結(jié)尾牌一樣,就肯定不會(huì)錯(cuò),剩下的步驟全是障眼法!