亚历克斯有时候是对的


50

这个挑战是要唤起我们通常是错误的 mod A. Alex A.的精神。


假设您有一个名为Alex的朋友,他需要有关基本逻辑和数学(特别是数学等效性)的帮助

他为您提供了以下形式的方程式列表,[variable] = [variable]其中a [variable]始终是单个大写字母A到Z(不是小写字母,数字,也没有其他内容)。列表中每行只有一个方程,只说了一行therefore

上面的所有方程式therefore都是前提,假设事实成立。下面的所有等式therefore都是未经验证的命题,这是Alex试图从前提推论得出的事实,它们可能是正确的也可能不是正确的。

例如,在此等式列表中,单个结论命题A = C恰好是正确的:

A = B
B = C
therefore
A = C

告诉亚历克斯,如果他的所有命题在逻辑上都遵循给定前提,这是您的工作。也就是说,您需要在结论中告诉Alex他是对还是错。

编写一个程序/函数,该程序/函数接受所描述的一系列方程式的字符串并打印/返回

Alex is right

如果所有结论都从前提逻辑上得出,否则输出

Alex is wrong

如果在逻辑上没有从前提得出任何结论。

以字节为单位的最短代码获胜。

确保注意以下情况:

  • 变量始终等于自己。例如

    B = A
    therefore
    A = A
    X = X
    

    结果Alex is right

  • 关系未知的变量不能假定相等。例如

    P = Q
    therefore
    E = R
    

    结果Alex is wrong

  • 如果之后没有方程式,therefore则结论是虚空的。例如

    D = C
    therefore

    therefore

    都导致Alex is right

  • 如果之前没有方程式,therefore则只能推断出自等式。例如

    therefore
    R = R
    

    结果Alex is right,但

    therefore
    R = W
    

    结果Alex is wrong

更多例子

亚历克斯是错误的情况:(用空行分隔)

A = B
C = D
therefore
A = C

A = L
E = X
A = I
S = W
R = O
N = G
therefore
G = N
L = I
R = O
S = A
X = X
X = E

D = K
D = Q
L = P
O = L
M = O
therefore
K = L

A = B
therefore
B = C

Z = A
S = S
therefore
A = Z
A = A
S = A
A = S
Z = A
Z = A

K = L
K = X
therefore
X = P
L = X
L = P

therefore
A = B
B = C
A = C

therefore
A = A
B = B
C = C
D = D
E = E
F = F
G = G
H = H
I = I
J = J
K = K
T = I
L = L
M = M
N = N
O = O
P = P
Q = Q
R = R
S = S
T = T
U = U
V = V
W = W
X = X
Y = Y
Z = Z

A = B
B = C
C = D
D = E
E = F
F = G
G = H
H = I
I = J
J = K
K = L
L = M
M = N
N = O
O = P
P = O
Q = R
R = S
S = T
T = U
U = V
V = W
W = X
X = Y
Y = Z
therefore
A = Z

therefore
C = D
T = Y
A = Z

P = Q
therefore
E = R

therefore
R = W

亚历克斯是正确的情况:

H = J
therefore
J = H

K = L
K = X
therefore
L = X

C = B
B = A
therefore
A = B

K = L
K = X
K = P
therefore
L = X
L = P
X = P

A = Y
Y = Q
Q = O
therefore
O = Y
O = A

C = C
therefore
C = C

A = B
B = A
therefore
A = B
B = A

A = B
B = C
C = D
therefore
A = A
A = B
A = C
A = D
B = A
B = B
B = C
B = D
C = A
C = B
C = C
C = D
D = A
D = B
D = C
D = D

therefore
A = A
B = B
C = C
D = D
E = E
F = F
G = G
H = H
I = I
J = J
K = K
L = L
M = M
N = N
O = O
P = P
Q = Q
R = R
S = S
T = T
U = U
V = V
W = W
X = X
Y = Y
Z = Z

D = I
F = H
J = M
therefore
M = J
D = I
H = F

A = B
B = C
C = D
D = E
E = F
F = G
G = H
H = I
I = J
J = K
K = L
L = M
M = N
N = O
O = P
P = Q
Q = R
R = S
S = T
T = U
U = V
V = W
W = X
X = Y
Y = Z
therefore
Z = A
F = R
G = I
W = L

A = B
B = C
therefore
A = C

B = A
therefore
A = A
X = X

P = P
C = G
M = C
therefore

D = C
therefore

therefore

therefore
R = R

42
PHP,13个字节Alex is wrong验证所有测试用例。
丹尼斯

19
嘿,有时候总比没有好。¯\ _(ツ)_ /¯
亚历A.


7
therefore\nTABS < SPACES->Alex is right
门把手

7
喜欢在序言中看到解决方案。
azz

Answers:


18

果酱(49)

"Alex is "qN%S{f{)er}(_el-}h;{)#},"wrong""right"?

受历史学家的Ruby解决方案启发。在线尝试
3字节,感谢jimmy23013 :)

说明:

对于每个前提,程序在其余文本中将第一个变量替换为第二个变量。然后,它检查是否有不同变量的结论。

"Alex is "    first push the part we know
qN%           read the input and split into lines
S             push a space (initial no-op replacement string, see below)
{…}h          do-while
  f{…}        for each line and the replacement string
    )         take out the last character
    er        replace the remaining character(s) with that character
  (           afterwards, take out the first line
  _el         duplicate and convert to lowercase
  -           remove all the resulting characters from the line
               this removes all lowercase letters and non-letters
               "X = Y" becomes "XY" (new replacement string)
               and "therefore" becomes "" (ending the loop)
              this is the loop condition and is left on the stack every time
;             after the loop, pop the empty string (from "therefore")
{…},          filter the remaining (conclusion) lines using the condition block
  )           take out the last character
  #           find its index in the remaining string
               this is 0 (false) iff the first character is the same as the last
              afterwards, we have an array of lines with non-equal variables
"wrong"       push "wrong"
"right"       push "right"
?             choose "wrong" if the array was not empty, else choose "right"

旧版本,85

"Alex is "26,:A;{:i{{_A=_@-}g}%$~}:F;0q"= "-'t/Nf%~\{A\Ft:A;}/1>{F=}%-"right""wrong"?

这使用联合查找算法。在线尝试


1
"Alex is "qN%S{f{)er}(_el-}h;{)#},"wrong""right"?
jimmy23013 2015年

1
我刚刚读完最后一行,内容为“这使用了独角兽搜索算法”……等等?xD
1

Alex is * wrong * right * ?
查理

32

红宝石,80 76 + 2 = 78

使用命令行标志p0,运行

gsub$1,$2%p=$`[/e/]while~/(.) = (?!\1)(.)/
$_="Alex is #{p ?:wrong: :right}"

说明:

这使用纯字符串操作。p0将完整的输入作为单个字符串读取到变量中$_。然后,我们将该字符串与正则表达式重复进行匹配,该正则表达式/(.) = (?!\1)(.)/查找格式为“ X = Y”的所有字符串,其中X和Y不是同一字母,并将X分配给$ 1,Y分配给$ 2。找到这样的匹配项后,gsub$1,$2将字符串中的所有X实例替换为Y。我们还会检查此匹配是发生在“因此”之前还是之后

$`[/e/]

如果事后发生,则是不合理的主张,而Alex是错误的。我们使用跟踪是否发生了此类情况p=。采用p作为跟踪变量防止东西从打破如果循环嗟甚至有一次,因为p将返回nil,如果它没有被分配到。

截至本文发布时,CJam解决方案更长。值得骄傲的时刻,尽管无疑是短暂的时刻。

编辑:是的,很快就废位了。同样,为了完成说明,在执行结束时使用p标志$_输出最终值,因此最后一行是输出。


15
最甜蜜的时光是在一个人被残酷的人杀死之前。
Alex A.

滥用String#formatgsub调用和赋值到一个表达式中是一个很不错的主意,+ 1!
Ventero

12

CJam,83 75 68 67 64字节

感谢Dennis节省了1个字节。

"Alex is "q_elN--N/:$La/~{)-},\{__m*{:&},::^|}5*-"wrong""right"?

测试套件。测试用例对于永久链接而言太长,因此只需从问题中复制它们即可。请注意,这相当慢-在线解释器需要一两分钟。通过更改5*2*这种情况,您几乎可以立即完成并解决除一个测试用例以外的所有问题,从而可以使其更快。

说明

(有点过时。)

这样做的目的是对可能的等式进行“大量填充”,然后删除从结论列表中获得的所有等式。可以证明,洪水填充不超过5个步骤,因为这些步骤将覆盖一个距离(在不等式的初始图中),但最大距离为25。25 = 32

"Alex is " e# Push the string.
q          e# Read the input.
_elN-      e# Make a copy, convert to lower case, remove linefeeds. This gives us a string
           e# with all the characters we don't want from the input.
-          e# Remove them from the input. This leaves two upper-case letters on each line
           e# and an empty line between premises and conclusions.
N/         e# Split into lines.
La/        e# Split around the empty line.
~          e# Dump both halves on the stack.
{)-},      e# Remove any "A = A"-type equalities from the conclusions.
\          e# Swap with the premises.
{          e# Extend the premises 5 times...
  _Wf%     e#   Duplicate the premises and reverse each one, because = is symmetric.
  |        e#   Set union with the original premises.
  __m*     e#   Make two copies and get an array of every possible pair of premises.
  {:&},    e#   Select those which have at least one character in common.
  ::^      e#   For each such pair, take the mutual set difference, i.e. those characters
           e#   that are in only one of the strings.
  |        e#   Set union with the original premises.
}5*
-          e# Remove all the equalities we've obtained from the conclusions. If all
           e# conclusions were valid, the result will now be a empty array, which is falsy.
!          e# Logical not.
"wrong""right"?
           e# Select "wrong" or "right", respectively.

构建传递闭包,是吗?我对CJam不熟悉,但似乎第5代平等可能只在一个方向上产生。如果是这样,则需要再进行一次迭代来逆转这些相等性。
user2357112

@ user2357112我认为它们应该在两个方向上生成,因为第一步将输入的所有反向添加(或者在进一步研究的版本中,我将所有前提和结论均等排序)。
Martin Ender

但是,当您采用对称差异时,您是否在两个方向上都得到了边?(或者,在进一步简化的版本中,对称差异是否会在所需的方向上生成边缘?)
user2357112 2015年

@ user2357112由于我正在处理整个笛卡尔乘积,因此我将获得两种顺序的每一对相等性,这将导致得出结论的两种顺序(我需要显式反转或对初始输入进行排序的唯一原因是原始前提不一定是在此过程中生成的,因此不会因采用笛卡尔乘积的集合差异而将它们反转。
马丁·恩德

6

R,183192字节

我已经修改了答案,以解决user2357112指出的限制。当亚历克斯实际上是正确的时候,仍然有极小的可能性将他召唤出来(如果我理解挑战的背景,这本身似乎不会经常发生:-)。我希望他不会介意。

i=grep("t",z<-scan(,"",,,"\n"))
cat("Alex is",if(eval(parse(t=c(paste(LETTERS,"=",1:26),sample(rep(head(z,i-1),1e3)),paste(c(TRUE,sub("=","==",tail(z,-i))),collapse="&")))))"right"else"wrong")

我需要对此打一点:

lines = scan(, what = "", sep = "\n")
therefore_idx = grep("therefore", lines)
setup = paste(LETTERS, "=", 1:26)
premises = sample(rep(head(lines, therefore_idx - 1), 1000))
propositions = paste(c(TRUE, sub("=", "==", tail(lines, -therefore_idx))), collapse = "&")
things_to_evaluate = c(setup, premises, propositions)
boolean_result = eval(parse(text = things_to_evaluate))
cat("Alex is", if (boolean_result) "right" else "wrong")

例如,如果输入是

A = B
B = C
therefore
A = C
B = C

它将首先评估setup

A = 1
B = 2
...
Z = 26

然后 premises

A = B
B = C

将以随机顺序运行1000次。这是为了确保(“几乎确定”)所有平等都得到传播。最后,它将评估propositions

TRUE & A == B & B == C

3
如果前提是A = B, B = C, C = A,则值将永远循环。仅进行26轮评估就不够了。
user2357112

我的逻辑失败了……感谢您的示例,然后我将不得不做其他工作。
flodel,2015年

我认为已经解决了,或者几乎...!
flodel 2015年

5

Haskell,208个字节

import Data.Equivalence.Persistent
c l=equate(l!!0)$last l 
r=foldr(c)$emptyEquivalence('A','Z')
l#r=equiv r(l!!0)$last l
f x|(h,_:t)<-span((<'t').head)$lines x="Alex is "++if all(#r h)t then"right"else"wrong"

我将工作转移到Data.Equivalence.Persistent模块上,该模块提供用于操纵等效类的函数。剩下要做的就是解析输入和调用函数,这些函数有时名称太长,无法正确打高尔夫球。

用法示例:

*Main> f "A = B\nB = C\ntherefore\nA = C"
"Alex is right"

*Main> f "A = B\nB = D\ntherefore\nA = C"
"Alex is wrong"

3

Mathematica,182

f[s_]:="Alex is "<>If[True===And@@Simplify[#2,#1]&@@(StringSplit[s,"\n"]/.{a___,"therefore",b___}:>StringSplit/@{{a},{b}}/.{x_,_,y_}:>Symbol[x<>"$"]==Symbol[y<>"$"]),"right","wrong"]

根据挑战在字符串输入上工作。

In[]:= f["A = B
B = C
therefore
A = C"]
Out[]= Alex is right

In[]:= f["D = K
D = Q
L = P
O = L
M = O
therefore
K = L"]
Out[]= Alex is wrong

您可以通过声明失去8个字节f作为一个纯函数,替换Simplify[#2,#1]#2~Simplify~#,和更换StringSplit[s,"\n"]#~StringSplit~"<actual newline>"
LegionMammal978

好点!还可以q=StringSplit;,然后将s / StringSplit / q /保存另外6个字节。但是最后,即使逻辑字符看起来很合适,这恐怕对于Mathematica也不是一个好挑战。

另外,a___b___可能可以更改为a__b__,和s=Symbol;
LegionMammal978

a__并且b__即使前提,命题或两者都为空也不会起作用

3

视网膜,90字节

要运行,请将以下12行代码放置在12个单独的文件中(每个文件在第一个文件后计+11字节)。<empty>指定一个空文件;\n指定文字换行符。或者,将\ns 保持不变,将所有行放在一个文件中,然后使用该-s选项。确保所有文件使用文字换行符,而不是Windows \r\n,并注意最后一行末尾的空格。

s+`^(.) = (.)(.*)\1
$1 = $2$3$2
)`^. .+\n
<empty>
^.+|(.) = \1
<empty>
^\n*$
right
^[^r]+
wrong
^
Alex is 

这个怎么运作

每当前提的lhs稍后出现在文件中时,第一个替换匹配输入中的第一个前提。它用前提的rhs代替了以后发生的情况。该+修改确保更换反复进行,直至它不匹配了。因此,如果第一个前提为A = BA则文件中的所有后续s都将转换为Bs。

第二个替换项从输入中删除了第一个前提,因为我们现在已经完成了它。然后,)修饰符循环回到第一个替换项,并重复进行直到整个循环的整个过程都没有变化。当所有前提都已被替换和删除并且输入以开头时,就会发生这种情况therefore

第三次替换匹配输入的第一行(即therefore)或任何形式的A = A,并将其删除。如果所有命题都得到前提的支持,那么所有命题都将与该表单匹配,因此剩下的应该仅由换行符组成。第四个替换将其更改为right。否则,第五次替换会将所有剩余的内容(rtherefore删除以来不包含在内)更改为wrong。最后,最后的替换Alex is 在开头添加。


3

Python 2,264字节

mbomb007已经有了一个出色的Python 3答案。这个答案从那个人那里窃取了(特别是“ Alex is wrriognhgt”把戏)。

而且这个答案也比那个答案长得多...

好吧,无论如何,此答案的想法是维护一个键值对字典,其中键是26个大写字母字符,每个键的对应值是与该键等效的一组字母。(如果所有26个字母都相等,则每个键的对应值将有一组26个字母。)

def a(s):
 d={C:set(C)for C in map(chr,range(65,91))};p,c=s.split('t');c,p=[x.split('\n')for x in[c[9:],p]]
 for u in p[:-1]:
    g,h=u[::4];y=d[g]|d[h]
    for v in y:
     for w in y:d[v]|=d[w];d[w]|=d[v]
 print'Alex is','wrriognhgt'[all(u[0]in d[u[4]]for u in c if u)::2]

(为了节省字节,此答案将空格和制表符混合使用,这在Python 2中是合法的。)

此代码非常有效,因为字典被限制为最大可能的大小(如上所述,由26乘26表示),该大小不取决于输入的行数。

现在,当我在研究这种解决方案时,我意识到我可以通过使用字符串代替字典值的集合来节省四个字节,方法是替换

d={C:set(C)for C in map(

d={C:C for C in map(

当然,您还必须|用字符串串联替换(注意:不要这样做)set union操作的三个实例+,但这不会改变代码长度。结果是,一切都应该仍然一样工作,只不过它不会像设置集合一样消除重复项(它只会不断添加到字符串的末尾)。听起来还可以-效率肯定要低一些,但是是260个字节而不是264个字节。

好吧,事实证明260字节的版本效率很低,以至于MemoryError当我使用

A = B
A = B
therefore
B = A

这让我感到惊讶。 让我们研究一下260字节的“字符串连接”版本!

当然,它会从键值对A:AB:B(再加上其他24个无关紧要的)开始。我们写d[A]的意思是对应于键的字典值A,因此一开始就拥有了d[A] = A。现在,在给出前提的情况下A = B,它将从连接值d[A]=Ad[B]=Bget开始y = AB。然后它将遍历此字符串两次:for v in AB: for w in AB:...

因此,第一次通过循环,我们有v=Aw=A。应用d[v] += d[w]d[w] += d[v]生成以下顺序的词典:

{A:A, B:B}      (start)
{A:AA, B:B}     (d[A] += d[A])
{A:AAAA, B:B}     (d[A] += d[A])

接下来,使用v=Aw=B

{A:AAAA, B:B}     (start)
{A:AAAAB, B:B}    (d[A] += d[B])
{A:AAAAB, B:BAAAAB}   (d[B] += d[A])

接下来,v=B, w=A

{A:AAAAB, B:BAAAAB}   (start)
{A:AAAAB, B:BAAAABAAAAB}     (d[B] += d[A])
{A:AAAABBAAAABAAAAB, B:BAAAABAAAAB}     (d[A] += d[B])

v=B, w=B

{A:AAAABBAAAABAAAAB, B:BAAAABAAAAB}     (start)
{A:AAAABBAAAABAAAAB, B:BAAAABAAAABBAAAABAAAAB}     (d[B] += d[B])
{A:AAAABBAAAABAAAAB, B:BAAAABAAAABBAAAABAAAABBAAAABAAAABBAAAABAAAAB}     (d[B] += d[B])

上面的步骤序列将实现一个前提A = B,结论A是等于字符串中的每个字母AAAABBAAAABAAAAB,而B等于字符串中的每个字母BAAAABAAAABBAAAABAAAABBAAAABAAAABBAAAABAAAAB

现在,假设接下来的前提是A = B 再次。您首先计算y = d[A] + d[B] = AAAABBAAAABAAAABBAAAABAAAABBAAAABAAAABBAAAABAAAABBAAAABAAAAB

接下来,您将该字符串循环两次:for v in y: for w in y:...

是的 也许那不是一个非常有效的实现。


我的回答不是“很好”,因为它是无效的,但这是一个值得注意的尝试。太糟糕了,我无法正常工作。
mbomb007'2015-10-26

1
@ mbomb007呵呵,很抱歉听到这个消息。(我确实认为您有一个很酷的方法!)由于您反对“伟大”一词,所以我用“卓越”代替。:)
mathmandan

2

ES6,128个字节

松散地基于Ruby版本。

r=s=>(m=/^[^e]*(.) = (?!\1)(.)/.exec(s))?r(s.replace(RegExp(m[1],'g'),m[2])):'Alex is '+(/(.) = (?!\1)/.test(s)?'wrong':'right')

在“因此”之前查找所有非自等式,并每次都在整个字符串中认真地替换该变量(这样可以在while循环中节省字节)。


1

C,240字节

#define V[v-65]
v[26];char*r[]={"wrong","right"};i=65;j;g(a){return a V^a?g(a V):a;}main(){char b[16];for(;i<91;++i)i V=i;while(gets(b)&&*b<99)b[0]V=b[4]V=b[0]V<b[4]V?b[0]V:b[4]V;while(gets(b))j|=g(*b)^g(b[4]);printf("Alex is %s\n",r[!j]);}

这是通过将值组合到集合树中来实现的,因此任何等效值都会导致相同的集合根。取消引用,将隐式类型设为显式。

// Anything before `V` becomes an index into `v`, offset by -'A'.
#define V [v-65]
int v[26];
char* r[] = {"wrong", "right"};
int i=65;
int j;
// Finds a set identifier for a by recursing until some index points to itself.
int g(int a) {
    return a V ^ a
           ? g(a V)
           : a;
}
int main() {
    char b[16];
    // Initialize all entries to point to themselves.
    for(; i < 91; ++i)
        i V = i;
    // For each premise "A = B", set the entries for A and B to point to the
    // smaller of their current values. This exits after reading "therefore"
    // as 't' > 99.
    while (gets(b) && *b < 99)
        b[0]V = b[4]V = b[0]V < b[4]V
                        ? b[0]V
                        : b[4]V;
    // For each conclusion "A = B", OR j with non-zero if the set identifiers
    // for A and B are different.
    while (gets(b))
        j |= g(*b) ^ g(b[4]);
    printf("Alex is %s\n", r[!j]);
}

180字节

此较短的版本适用于OP中的所有情况,但对于某些其他输入错误地声称Alex是错误的。它使用类似的方法,但是对于每个前提,只需将第二个条目设置为第一个条目的当前值即可。比较时,它仅查找精确值,而不查找树。

v[26];*V=v-65;char*r[]={"wrong","right"};i;j;main(){char b[16];for(;i<26;++i)v[i]=i;while(gets(b)&&*b<99)V[b[4]]=V[*b];while(gets(b))j|=V[*b]^V[b[4]];printf("Alex is %s\n",r[!j]);}

输入失败的示例:

A = B
C = B
因此
A = C


1

05AB1E,32 字节

…±º€ˆ „–у©#|€á[ćD.l#`:}\€ËPè«.ª

受到@aditsu的CJam答案的启发

在线尝试验证所有测试用例

说明:

…±º€ˆ      # Push dictionary string "alex is "
„–у©      # Push dictionary string "wrong right"
     #     # Split by spaces: ["wrong","right"]
|          # Push all input-lines as list
 ۈ        # Only leave the letters of each line
   [       # Start an infinite loop:
    ć      #  Extract the head of the list; pop and push remainder-list and head separately
     D     #  Duplicate the head
      .l   #  If it's a lowercase string:
        #  #   Stop the infinite loop
    `      #  Push both letters in the string to the stack
     :     #  Replace all these letters in the remainder-list
 }\        # After the infinite loop: discard the duplicated "therefore"
          # For each letter-pair in the remainder list of condition-lines:
    Ë      #  Check if both letters are equal (1 if truhy; 0 if falsey)
   P       # Check if everything was truthy by taking the product
    è      # Use this to index into the earlier ["wrong","right"]-list
     «     # Append it to the "alex is " string
         # Sentence capitalize it
           # (after which the result is output implicitly)

请参阅我的05AB1E技巧(如何使用字典?部分),以了解为什么…±º€ˆ"alex is "„–у©"wrong right"


0

bash + awk + SWI-Prolog,167字节

head -n1 <(awk '/therefore/{s=1;next};{if(s)print"?=("$1","$3")";else print};END{print"write(\"Alex is right\");write(\"Alex is wrong\"). halt."}' -|paste -sd ,|swipl)

在线尝试!

最初,这只是Prolog的答案,但是我可以找到的将输入格式实际转换为可用格式的工具非常有限,即使我几乎没有经验,我还是决定用bash来完成其中的一部分。用bash做任何事情,甚至从未接触过awk。我最终花了足够的时间在它上发布它,甚至在它增长到这个167字节后,几乎对所有怪物都不感兴趣。

从本质上讲,awk程序的作用是把输入从标准输入,删除与行therefore,每次更换A = B用后?=(A,B),并追加write(\"Alex is right\");write(\"Alex is wrong\"). halt.。然后,paste -sd ,用逗号替换除最后一行以外的所有换行符,将其转换为对SWI-Prolog Shell的两个有效查询,然后运行该查询,并用将该打印结果截断为一行head -n1,这需要<(...)而不是管道,原因是我的理解。所有这些,仅需使用内置功能即可

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.