编程智慧表明,将循环编程任务转变为线性编程任务(矢量化)可以缩短计算时间。但这是真的吗?在什么条件下?多少?挑战 1 提供了一个很好的机会来找出答案。为了回答这四个挑战问题,我决定对于每组七个字母,我会:
- 计算每个字母在字典单词中出现的次数。这让我找到了第 2、3 和 4 项的答案。
- 确定一个单词是否包含不在七个字母集中的字母。这是第 1 项所需要的。
挑战 1 尝试 1.JSL使用基于Length和Regex函数的列公式进行计数。使用了一些附加的列公式以使代码更易于阅读。唯一的循环用于重新评估每个字母集的函数。运行时间略低于 29 秒(所有时间均为 10 次运行的平均值)。不错,但也不是很好。是公式的复杂性还是因为公式评估应该使用循环(在列表或矩阵上)而不是列来完成?我使用test simple.JSL和test full.JSL进行调查。除了比较列与循环之外,我还想看看直接从表或列表中读取输入值是否更快。第一次将表中的值读取到列表或矩阵中是否会增加时间?
下面的结果(以秒为单位)表明,随着公式变得更加复杂,公式和循环之间的差异越来越不明显。用于构建公式的函数对时间的影响要大得多。常规搜索函数( Regex和Loc )比特定搜索函数( Contains和Substr )慢得多。
|
功能
|
公式
|
列表+列表访问
|
列表+表访问
|
|
长度
|
0.0525
|
0.1398
|
0.1311
|
|
地方 + 项目
|
0.4187
|
0.5462
|
0.5383
|
|
Length + 正则表达式
|
9.3744
|
9.2745
|
9.3911
|
|
循环+包含+子字符串
|
3.3255
|
3.4139
|
3.5444
|
将这些知识付诸实践,我创建了两个新脚本。挑战 1 尝试 2.JSL使用公式中包含一个循环的公式来计算字母数。挑战 2 尝试 3.JSL使用列表而不是公式。它还利用了这样的条件:对于满足第 1、2 和 3 项的任何单词,它必须至少包含一次所有七个字母组字母。运行时间分别为 15.50 秒和 10.40 秒。在这种情况下,循环可能更快,因为需要评估的函数更少。这两种改进都比我最初的尝试要好得多,但仍然不是最快的。改进必须来自更快的函数、更少的循环/函数评估或回答挑战问题的不同方法。由于前两种方法似乎不太可能产生富有成效的可能性,因此寻求一种不同的方法。
挑战 1 Pattern Matching.JSL脚本来自@Craige_Hales 。 Craige 最近从 JMP Development 退休,但在社区中仍然非常活跃。他负责JMP中的模式匹配和正则表达式功能。 Craige 的解决方案通过首先将字典列表转换为长字符串来向量化问题,然后应用模式匹配将单词解析到解决方案桶中。它的运行时间为 3.71 秒,提供了模式匹配的广度和灵活性的一个很好的例子。最快的解决方案来自@brady_brady ,耗时 2.76 秒。他没有将每个字母集应用于字典单词,而是首先计算每个字典单词中每个字母的出现次数。虽然这最初将字母比较的数量从 7 个增加到 26 个,但只需要进行一次,从而避免了对每个新字母集的重新评估。通过从初始字母矩阵中提取适当的列来找到答案单词,从而获得更快的解决方案。它也是最短的剧本,只有 1146 个字符。
这是第一次挑战后的排行榜:
请继续关注 5 月 1 日推出的挑战 2!
@MathStatChem在计算字符数之前,我删除了注释和空格。 在当前状态下,您的新脚本在我的计算机上运行大约需要 3.6 秒。 比布雷迪慢,但比克雷奇快。 一些小的改变就带来了相当大的差异! 不过,有些问题的答案并不正确。 以下是我使用的字母集以及答案。
//acehlot
1. {"acacatechol"}
2. {"scientificophilosophical"}
3. {"hypercivilization"}
4. {"macracanthrorhynchiasis", "pancreaticoduodenostomy", "pericardiomediastinitis"}
//ilosuvx
1. {"lixivious"}
2.{"subclavioaxillary"}
3.{"excitovascular"}
4. {"formaldehydesulphoxylate"}
//mnoruwy
1.{"unwormy"}
2.{"countrywoman", "journeywoman", "laundrywoman"}
3.{"jurywoman"}
4.{"formaldehydesulphoxylate"}
新旧脚本的长度分别为 2461 和 2469。
所以我使用了与@brady_brady ,但我最初的字母计数代码效率非常低,使用了更多循环和对列表的调用。我替换了他的方法,果然我的脚本运行了大约 3 秒。很酷!
函数 items() 对此至关重要。我没有意识到它会这样做。将字符串转换为其各个字符的列表(按顺序,重复的字母作为列表中的单独项目),只需
项目(字符串);
所以 items("easypeasy","") 返回 {"e", "a", "s", "y", "p", "e", "a", "s", "y"}。
然后使用 contains 来引用字母表字母的索引也是一个非常好的技巧。
contains("abcedfghijklmnopqrstuvwxyz","a") 返回 1,
contains("abcedfghijklmnopqrstuvwxyz","b") 返回 2, ...
我使我的代码和日志输出更加详细,不确定您是否在计算字符之前删除了注释。
这是我的代码(使用更快的方法进行了改进):
startTime = Time Of Day( Today() );
letterSetList = {"apouter", "abdcefl", "acouter", "rstlneo", "efghijk"};
alphabet = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z"};
abet="abcedfghijklmnopqrstuvwxyz";
_dt = Current Data Table();
numwords = N Rows( _dt );
_c = Column( _dt, "Words" );
wordlist = _c << get values;
// get lengths of each word in word list
// also create a matrix indicating how many times each letter in the alphabet is used in each word
wordlengths = J( numwords, 1, 0 );
alphacounts = J( numwords, 26, 0 );
For( ii = 1, ii <= numwords, ii++,
its=items(wordlist[ii],"");
wordlengths[ii] = Length( wordlist[ii] );
for(jj=1, jj<=nitems(its), jj++, alphacounts[ii,contains(abet,its[jj])]++);
);
// also create a matrix indicating if a letter in the alphabet is used in each word
alphaindex = alphacounts > 0;
For( ll = 1, ll <= N Items( letterSetList ), ll++,
str = letterSetList[ll];
// create an indicator vector for the letters in the string
str_alphaindex = J( 1, 26, 0 );
For( jj = 1, jj <= 26, jj++,
For( kk = 1, kk <= Length( str ), kk++,
str_alphaindex[1, jj] = str_alphaindex[1, jj] + (Substr( str, kk, 1 ) == alphabet[jj])
)
);
// 1 The longest word using all seven letters and only those seven letters. Letters may be used more than once.
// words that meet this criteria will have a "zero distance" from their letter indicator row and the string letter indicator vector
pickrow1 = Distance( alphaindex, str_alphaindex ) == 0;
// now find the word that has the longest length
pickrow1 = pickrow1 & (wordlengths == Max( wordlengths[Loc( pickrow1 )] ));
Print( "1. Find longest word(s) that uses only the letters <" || str || "> (item " || Char( ll ) || " from itemSetList)" );
If( Dim( Loc( pickrow1 ) )[1],
Print( wordlist[Loc( pickrow1 )] );
Print( "which is in the following row(s) from the Word List table " );
Print( Loc( pickrow1 & (wordlengths == Max( wordlengths[Loc( pickrow1 )] )) ) );
,
Print( "no word(s) found" )
);
Print( "-------------------" );
// 2 The longest word using all seven letters and any other letters.
// words that meet this condition, the sum of their letter indictor rows for the letters in the string should equal 7
pickrow2 = (V Sum( alphaindex[0, Loc( str_alphaindex )]` )` == 7);
// now find the word that has the longest length
pickrow2 = pickrow2 & (wordlengths == Max( wordlengths[Loc( pickrow2 )] ));
Print(
"2. Find longest word(s) that use all letters <" || str || "> (item " || Char( ll ) ||
" from itemSetList) and/or any other letters"
);
If( Dim( Loc( pickrow2 ) )[1],
Print( wordlist[Loc( pickrow2 )] );
Print( "which is in the following row(s) from the Word List table " );
Print( Loc( pickrow2 ) );
,
Print( "no word(s) found" )
);
Print( "-------------------" );
// 3 The longest word using all seven letters and any other letters where no letter
// from the set of seven is used more than once.
// words that meet this condition, meet the condition for the 2nd rule but also the sum of their letter counts for
// the letters in the string has to be equal to 7
pickrow3 = (V Sum( alphaindex[0, Loc( str_alphaindex )]` )` == 7) & (V Sum( alphacounts[0, Loc( str_alphaindex )]` )` == 7);
// now find the word that has the longest length
pickrow3 = pickrow3 & (wordlengths == Max( wordlengths[Loc( pickrow3 )] ));
Print(
"3. Find longest word(s) that use all letters <" || str || "> (item " || Char( ll ) ||
" from itemSetList) only once and/or any other letters "
);
If( Dim( Loc( pickrow3 ) )[1],
Print( wordlist[Loc( pickrow3 )] );
Print( "which is in the following row(s) from the Word List table " );
Print( Loc( pickrow3 ) );
,
Print( "no word(s) found" )
);
Print( "-------------------" );
// 4 The longest word in which 5 of the 7 letters appears.
// Any letter including those outside of the 7 may appear once or more
// except the two unused letters from the list of 7, which may not appear at all.
// words that meet this condition have the sum of their letter indicators (for the letters in the string) equal to 5
pickrow4 = (V Sum( alphaindex[0, Loc( str_alphaindex )]` )` == 5);
// now find the word that has the longest length
pickrow4 = pickrow4 & (wordlengths == Max( wordlengths[Loc( pickrow4 )] ));
Print(
"4. Find longest word(s) that uses only 5 of the 7 letters <" || str || "> (item " || Char( ll ) ||
" from itemSetList) and/or any other letters"
);
If( Dim( Loc( pickrow4 ) )[1],
Print( wordlist[Loc( pickrow4 )] );
Print( "which is in the following row(s) from the Word List table " );
Print( Loc( pickrow4 ) );
,
Print( "no word(s) found" )
);
Print( "-------------------" );
);
endTime = Time Of Day( Today() );
Print( endTime - startTime );