罗兰谈MATLAB的艺术

将想法转化为MATLAB

请注意

罗兰谈MATLAB的艺术已退役,不会更新。

相似(或不相似)-第二次击败Wordle

今天的客座博主是Matt Tearle,他在创建我们在线培训内容的团队中工作,比如我们的各种 斜坡弯道课程 让你开始学习MATLAB, Simulink和其他应用。金宝app马特过去在这里写过几篇博客文章,通常是由一个谜题引起的——今天也不例外。

(名词:使不知所措;挡板)

Wordle。它抓住了我们所有人。当 亚当写了他的文章 关于用MATLAB来解决Wordle字谜,我一直在考虑做完全相同的事情。(在过去,我写过代码来作弊 和《纽约时报》 拼字比赛 谜题。)我看到其他朋友发过关于信件分发的帖子。我猜这就是书呆子干的事。
当我读到亚当的帖子时,我知道我必须看看我是否能做得更好。我的第一个想法是读者彼得·维滕伯格建议的:根据字母出现在单词中的位置来衡量它们的概率。然后,我尝试了一种与另一位读者TallBrian建议的方法类似的方法,根据单词在下一轮中减少可能性的程度来打分。我还尝试了如何选择新字母最多的单词。
但是什么都不管用。我无法对亚当94%的成功率做出任何显著改善。他注意到,Wordle官方标准中的一些单词并不在用于开发算法的集合中。我开始怀疑了。我的数据感官很刺激。

检查(名词:进行检查或调查;查询)

根据字母的概率,TARES是一个很好的开头猜测。但当我作为人类玩Wordle时,我觉得我还没见过太多以-S结尾的单词。也许是时候比较这两个单词集了。这里我只是重新利用亚当的代码来得到两个单词列表。字典里的单词(叫做 经常 在Adam的代码中)都在字符串数组中 trainwords .实际的Wordle列表(称为 mystery_words 在亚当的代码中)是流行的 testwords
[trainwords,testwords] = getdictionary;
名称大小字节类属性testwords 2315x1 125106 string trainwords 4581x1 247470 string
变量名显示了我的偏见:我现在把它当作一个机器学习问题来思考。一组单词被用来训练一个算法——在这种情况下,不是一个标准的机器学习方法,而是一个基于统计数据的定制算法。另一个是测试集。任何从事机器学习的人都知道,模型的质量在很大程度上取决于数据的质量。具体来说,你需要你的训练数据准确地表示你的模型将使用的实际数据。
“训练”和“测试”数据集的字母分布如何比较?首先,我需要计算每个字母在每个位置出现的次数(对于两组单词):
列出所有大写字母(a - z)
AZ = string(char((65:90)'));
计算字母的分布
lprobTrain =字母分布(训练词,AZ);
lprobTest =字母分布(testwords,AZ);
名称大小字节类属性AZ 26x1 1500 string lprobTest 26x5 1040 double lprobTrain 26x5 1040 double testwords 2315x1 125106 string trainwords 4581x1 247470 string
现在我有了两个26乘5的矩阵,表示字母表中每个字母在每个位置的频率。首先,让我们看看总体分布:
% 5个字母位置的平均值
distTrain = mean(lprobTrain,2);
distTest = mean(lprobTest,2);
%的阴谋
栏([distTrain,经销])
xticks(1点)
xticklabels (AZ)
xtickangle (0)
传奇(“培训”“测试”
ylabel (“用途比例”
标题(“信件分发总数”
哇,s出问题了。果然,这两个单词表的用法有很大区别。训练集中的d也比实际Wordle集中的d多。与此同时,Wordle集合中有几个字母比一般字典使用得更多——最显著的是R、T和Y。
现在我更怀疑以-S结尾的词了。让我们根据字母和单词位置来看看用法的全貌。如果您进行任何数据分析,您可能会遇到这种情况,希望将值可视化为两个离散变量的函数。在过去,你可能用过 显示亮度图像 为了这个,这当然是一个可靠的老工作。但是,如果您没有认真地阅读发行说明,您可能不会意识到一些新的数据分析图表功能,例如 的热图 (在R2017a中引入)。
热图(1:5,AZ, lprobTrain)
标题(《培训数据的信分发》
热图(1:5,AZ, lprobTest)
标题(“TEST数据的字母分布”
啊,甜蜜的表白!当然,我们的策略所基于的单词分布与实际Wordle单词中常用字母的分布不同。特别要注意的是,Wordle更有可能以S开头,而不是以1结尾。Es出现的位置分布也不一样。为了进行这些类型的比较,可能更容易可视化两个热图之间的差异:
取分布的差值
delta = lprobTrain-lprobTest;
找到最大的,以对称地设置颜色限制
Dl = max(abs(delta(:)));
%可视化
hm =热图(1:5,AZ,delta,“ColorLimits”, dl * [1],“Colormap”、涡轮);
标题([“字母分布的差异”“红色=训练集中较高的患病率”"蓝色=测试集中较高的患病率"])
注意,我已经设置了颜色限制,因此0是“中性”绿色,而蓝色或红色显示了两个方向上的差异。- s单词的缺失很明显,单词末尾从- e -到- e的变化也很明显。总之,这意味着Wordle列表中更少的复数(如WORDS)和-ES动词形式(如“Adam LOVES playing Wordle”)。
这里还有一些其他细节,但它们很难看到,因为一切都被-S单词的差异所主导。让我们手动缩小颜色范围以查看更多细节。这样- s就不那么重要了,不过没关系,我们已经知道了。
嗯。ColorLimits = [-0.1 0.1];
现在我们可以看到一些有趣的趋势:更多的y字(可能是形容词,比如“这是一个愚蠢的博客帖子的主题”),更多的R和-T字,更少的d字(可能是-ED过去时,比如“直到亚当的帖子,马特喜欢玩Wordle”),更少的元音作为第二个字母的例子,R和S在位置1和2切换。
使用这个热图,你还可以看到TARES与训练(字典)集比测试(Wordle)集更一致:热图中的每个字母都有一个正值。因此,虽然字母总体上都是高概率的,但这种特定的安排对训练集特别好,对测试集特别坏。对STARE进行简单的重新排列就可以扭转这种情况。

旁白(名词:与正在讨论的主题没有直接关系的评论或讨论)

我注意到我的代码在 字符串 识字课 , 分类 .您可以在这里的代码中看到一些,但我的Wordle求解器在使用不同类型方面更加自由。这可能看起来像是糟糕编程的证据——“已经选择一个数据类型了!”,你会哭——但我认为这实际上是一个很好的实践:MATLAB给了你很多很棒的数据类型;使用它们!随着字符串(R2016b)的引入,我们会遇到这样的问题:“那么我们现在应该只使用字符串吗?”和“用char代替字符串有什么意义吗?”如果你对此感到困惑,这里有一个简单的原则:char的单位是单个字符,string的单位是任何长度的文本。Wordle是关于文字的…还有那些字母!这就是为什么同时使用string(用于学习单词)和char(用于学习字母)是很有用的。
此外,我们的开发团队也为我们提供了一大堆 方便的文本函数 还有弦。但是这些函数不仅仅是针对字符串的——就像许多MATLAB函数一样,它们接受不同种类的输入。它们接受任何形式的文本,并允许您在没有正则表达式的情况下进行基本的文本处理。例如,我假设Wordle不使用-ES和-ED动词形式。让我们来看看每个列表中有这些结尾的单词:
esdends = @(words) words(endsWith(words,[“西文”“ED”)));
ESDtrain = esdends(火车用语)'
ESDtrain = 1×619字符串
" abbes " " ached " " aches " " acmes " " acres " " acts " " added " " adzes " " aided " " aides " " ailed " " targeted " " angered " " aloes " " anted " " antes " " apses " " arced " " armed " " ashed " " ashes " " asked " " asses " " axles " " baaed " " babes " " baked " " bakes " " baled " " bales "
ESDtest = esdends (testwords)'
ESDtest = 1×23字符串
"有能力" "流血" "繁殖" "有能力" "有提示" "信条" "哭了" "干了" "埋了" "解放了" "炸了" "贪婪" "膝盖" "被挤了" "被撬了" "吓了" "速度" "被戳了" "有了马" "试了" "粗花呢" "没吃" "没嫁"
100 *元素个数(ESDtrain) /元素个数(trainwords)
Ans = 13.5123
100 *元素个数(ESDtest) /元素个数(testwords)
Ans = 0.9935
手巧的 endsWith 函数执行它所建议的,并找到具有给定结尾的单词。果然,来自字典的训练数据有许多对-ES和-ED动词,比如ACHED和ACHES(占整个列表的13.5%)。但Wordle中几乎没有一个单词是简单地在四个字母的动词后面加上-ED或-ES组成的。因此,-ED和-ES词的使用率要低得多(只有1%)。

后来(形容词:在以后的时间或阶段到来)

在确认字母分布确实不同之后,我用Wordle列表构建了各种求解算法,然后对它们进行测试,从而挽回了自己的自豪感。现在我99%的几率都能成功解决这个谜题。太好了。但也有一点不满意。任何数据科学家都知道,用相同的数据集进行训练和测试都是欺骗,不能很好地衡量你的算法在新数据上的表现。
但是…嗯,有 无新数据。完成Wordle单词列表的设置。因此,这仍然是一个有效的问题:鉴于官方Wordle列表,解决它的最佳方法是什么?
不幸的是,读者指出了亚当所做的一些细节(我遵循了这些细节)。这让人对我的“解决方案”产生了怀疑。金宝搏官方网站所以现在,我需要继续修补。如果 《纽约时报》并没有将Wordle隐藏在付费墙后面 等我想明白了,我就回来了。我甚至可能有足够的勇气进入互联网的每日争论:什么是最好的开始词?

回答(名词:用文字或书面形式给出回答;回应)

亚当的读者有一些聪明的想法,关于如何打败这个令人上瘾的游戏。你们谁有确定的开场白吗?你只花19.95美元就能透露给订阅者的秘密策略?你是怎么找到你的工作的,是什么让它这么棒?让我们知道 评论
函数[word5,mystery_words] = getdictionary
%拷贝自亚当F
%将单词列表读入字符串数组
R =阅读线(“https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words”);
使用附录中的自定义函数替换变音符号
Rs = removediacritics(r);
只保留以小写字母开头的条目
rs = rs(startsWith(rs,characterListPattern(“一个”“z”)));
去掉带有撇号的条目,比如缩写
Rs = Rs(~包含(Rs,“”));
Wordle使用全部大写字母
Rs =上(Rs);
%得到唯一的五个字母单词的列表
Word5 = unique(rs(strlength(rs)==5));
mystery_id =“1-M0RIVVZqbeh0mZacdAsJyBrLuEmhKUhNaVAI-7pr2Y”%取自上面链接的表单的URL
Mystery_url = sprintf(“https://docs.google.com/spreadsheets/d/%s/gviz/tq?tqx=out: csv”, mystery_id);
Mystery_words = readlines(mystery_url);
%有一组额外的双引号,所以让我们把它们去掉
Mystery_words = erase(”“”“);
%我们也用大写
Mystery_words = upper(Mystery_words);
结束
函数lprob = letterdistribution(words,AZ)
把单词分成单独的字母
字母=分裂(单词,"");
%这也会创建前导和尾空字符串,删除它们
Letters = Letters (:,2:end-1);
计算每个单词位置的字母分布
K = 1:5
lcount(:,k) = histcounts(categorical(letters(:,k),AZ));
结束
Lprob = lcount./sum(lcount);%正常化
结束
也源自亚当
引用:吉姆·古道尔,2020年。Stack Overflow,可从:https://stackoverflow.com/a/60181033获得
函数[clean_s] = removediacritics(s)
从文本中删除变音符号。
此函数从字符串中删除许多常见的变音符号,例如
á -重音
à -严肃的口音
â -回旋重音
% ü -变音,或trema或变音
ñ -波浪号
ç -雪松
å -环,或bolle
% ø -斜线,或实线,或贞线
%大写
S = regexp (S,‘(?:| | | | |一个)的“一个”);
S = regexp (S,“(?:Æ)”“AE”);
S = regexp (S,“(?:ß)”“党卫军”);
S = regexp (S,“(?:C)”“C”);
S = regexp (S,“(?:Ð)”' D ');
S = regexp (S,”(?:E E E | | | E)”“E”);
S = regexp (S,”(?:我| | |我)”“我”);
S = regexp (S,“(?:N)”“N”);
S = regexp (S,”(?:O O O O O | | | | |Ø)”“O”);
S = regexp (S,“(?:œ)”“OE”);
S = regexp (S,”(?:U | | | U)”“U”);
S = regexp (S,”(?:Y |ÿ)'“Y”);
%小写
S = regexp (S,‘(?:| | | | |一个)的“一个”);
S = regexp (S,“(?:æ)”“ae”);
S = regexp (S,“(?:c)”“c”);
S = regexp (S,“(?:ð)”' d ');
S = regexp (S,”(?:e e e | | | e)”“e”);
S = regexp (S,”(?:我| | |我)”“我”);
S = regexp (S,“(?:n)”“n”);
S = regexp (S,”(?:o o o o o | | | | |ø)”“o”);
S = regexp (S,“(?:œ)”“oe”);
S = regexp (S,”(?:u | | | u)”“u”);
S = regexp (S,(: | y)的“y”);
返回已清理的字符串
Clean_s = s;
结束
|
  • 打印
  • 发送电子邮件