谐音还是不谐音?


36

给定两个音符名称,您将编写一个程序来确定这两个音符形成的间隔是辅音还是不谐音。

介绍

在西方音乐中,只有12种“不同”的音调。它们的名称(从最低到最高)是:C, C#, D, D#, E, F, F#, G, G#, A, A#, B。该序列是循环的,即,它C在之后B无限地继续。

两个音调之间的距离称为间隔。上一个系列(例如C — C#E — F)中相邻的两个音符之间的间隔称为半音。距离较远的音符之间的间隔定义为从第一音调到第二音调所需的半音步数(同时可能环绕序列)。一些示例:D to E= 2个半音,C to G= 7个半音,B to D#= 4个半音(这环绕序列)。1个

现在,这些间隔分为两类:辅音(如果同时演奏两个音符,听起来会很悦耳)和不谐音(不是那么多)。

让我们将辅音间隔定义为:0、3、4、5、7、8和9个半音。

它们的其余部分是不和谐的,即:1、2、6、10和11个半音。

挑战

编写一个“程序”(通常在广义上来说:函数完全可以)执行以下操作:

  • 以两个音符名称(上述序列中的字符串)为输入。您可以按照自己的喜好选择它们(从stdin作为参数,用任何想要的分隔,甚至可以随意将它们作为字符列表(例如["C","#"])。但是,您不能为音符分配任何其他名称(尤其是您可能不会将它们从0编号到11,并使用数字)。

  • 对于在那里的音乐极客,音符将不指定八度。在这种情况下,纸币以什么顺序排列,哪个较低和哪个较高也无关紧要。最后,您不需要处理不在上面列表中的任何名称。没有其他类似的谐波E#,没有单位,没有双重变化等等。

  • 选择任意两个不同的值。只要输入中两个音符形成的间隔是辅音,您的程序就必须输出其中一个,否则输出另一个。(如果需要,可以是TrueFalse,但也可以是π和e :)

  • 这是一个代码高尔夫球。每种语言中以字节为单位的最短程序获胜。玩得开心!

示例和测试用例

Note 1    Note 2    Output    Interval [semitones]
  C          D     Dissonant   2
  A#         A#    Consonant   0
  G          D     Consonant   7 (wraparound)
  D#         A     Dissonant   6
  F          E     Dissonant   11
  A          C     Consonant   3

我没有添加更多的内容,因为在这种情况下没有特别危险的情况。

这是我的第一个挑战,因此热烈欢迎任何建设性的批评:-)。如果您发现理论解释草率,请随时提出问题。最后,请不要告诉我这是thisthis的重复。我确定不是。(后者非常相似,但更为复杂。我认为提出一些简单的挑战将使人们更容易加入。)


1:我尽力简化了这种解释。关于间隔的理论很多。请不要因为把它遗忘而ash我。

Answers:


12

果冻,21字节

将输入作为两个字符串的列表。返回0不谐调或1辅音。

OḢ6×%21_Lµ€IA“¬ɠṘ’æ»Ḃ

在线尝试!

OḢ6×%21_Lµ€IA“¬ɠṘ’æ»Ḃ   - main link
         µ€             - for each note             e.g. ["A#", "C"]
O                       -   convert to ASCII codes  -->  [[65, 35], 67]
 Ḣ                      -   keep the first element  -->  [65, 67]
  6×                    -   multiply by 6           -->  [390, 402]
    %21                 -   modulo 21               -->  [12, 3]
       _L               -   subtract the length     -->  [12, 3] - [2, 1] = [10, 2]
           IA           - absolute difference       -->  8
             “¬ɠṘ’      - the integer 540205
                  æ»    - right-shift               -->  540205 >> 8 = 2110
                    Ḃ   - isolate the LSB           -->  2110 & 1 = 0

由...制成

我们首先应该注意到,我们要寻找的函数F是可交换的:对于任何一对音符(A,B),我们都有F(A,B)= F(B,A)

由于没有太多可能的输入并且只有2个可能的输出要处理,因此必须有可能找到一个相当简单的哈希函数H,使得| H(A)-H(B)| 产生有限范围的值,并且相对于预期输出,所有可能的音符对(A,B)都不会发生冲突。

我们将测试函数H(mul,mod)的集合,其定义为:

H(mul, mod)(s) = ((ORD(s[0]) * mul) MOD mod) - LEN(s)

其中ORD(s[0])是注释的第一个字符的ASCII码,并且LEN(s)是注释的长度(如果有a ,则为2,否则为1)。'#'

下面是JS代码的注释版本,用于查找一对有效对(mul,mod)和生成的位掩码。有许多可能的解决方案,但是* 6 % 21这种方法最短。


3
您甚至如何提出这些问题?..您是通过手工还是蛮力获得了这种“算法”?并且不管第二个问题的答案如何:!?..:S“ 文字整数540205;右移(ASCII码;乘以6;模21;保持第一个;减去长度...);按位与1 ”。你的回答不断每次我留下深刻印象..
凯文Cruijssen

@KevinCruijssen我添加了用于查找这些值的原始JS代码。
Arnauld

感谢您的补充说明。我仍然像第一印象一样深刻,但是您对如何提出来进行了清晰的解释。太糟糕了,我只能投票一次。
凯文·克鲁伊森

9

APL(Dyalog)62 39字节

用途⎕IO←0; 0是辅音,1是辅音。将基本音符字符列表作为左参数,将尖音符列表作为右参数。

{⎕A[|-/('C D EF G A '⍳⍺)+⍵=⍕#]∊'BCGKL'}

在线尝试!

{} 匿名函数其中左边的参数是右边的参数

⎕A[... ]∊'BCGKL' 是一个 lphabet,通过下面的字符串中的一员索引?

  ⍕# 格式化根名称空间(产生尖锐的字符)

  ⍵= 正确的论点字符(尖锐字符)等于吗?

  ()+ 添加以下内容:

   'C D EF G A '⍳⍺ 字符串中左参数chars的索引

  -/ 那些之间的差异

  | 绝对值


您介意为不熟悉APL的我们的人增加解释吗?
Draconis

@Draconis添加了解释。
亚当

9

MATL30 27 26字节

,j'DJEFPGIALBC'&mQs]ZP7Mdm

在不同的行中输入两个音符。输出0为辅音,1为辅音。

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

说明

11个字符的字符串

DJEFPGIALBC

编码音符和不谐音间隔,如下所示。

程序首先在上述字符串中找到输入字符的从1开始的索引。非锐利的输入,例如Dwill Give 1EWill Give 3,...,Cwill Give 11。这些数字也可以视为1×1数字数组。像一把锋利的输入C#将给出1×2阵列[11 0],其含义C是在位置上发现11#没有被发现。

观察到字母JPIL永远不会出现在输入中。目前,它们仅用作占位符,例如,note E是上面的两个半音D。但是它们对于定义不协调的间隔也很有用。

1×1或1×2数组的第一项中的数字对应于半音的音高,不包括尖锐的符号(尚未)。请注意,这些数字所定义的标度并非始于C;但这没关系,因为我们只需要间隔,即音符之间的差异。减去获得的数字将得到间隔或12减去间隔。但是首先我们需要考虑尖锐的符号。

为了考虑尖锐音符,高尔夫球的方法(在MATL中)是将1先前获得的1×1或1×2数组的每个条目相加,然后对数组求和(2字节)。因此,非尖锐音符增加,1而尖锐音符增加2。根据需要,这会使锋利音符比非锋利音符高1个半音。我们还在所有音符上添加了一个额外的半音,但这不会改变它们之间的间隔。因此,现在注意D将给出音调编号2D#将给出3...,C将给出12C#将给出13

刺耳的间隔12610,或11。它们具有12模的对称性:当且仅当音符的反序模12的间隔是谐和的时,两个音符之间的间隔才是不谐调的。

如果我们计算字符串的连续差,'DJEFPGIALBC'我们将得到数值向量

6 -5 1 10 -9 2 -8 11 -10 1

除了一些负值外,它还精确地包含了不和谐的间隔,这既无用也不有害。请注意,选择JPIL字符串'DJEFPGIALBC'中的其他字母可以定义不连续的间隔(通过连续的差异)。

要查看两个输入音符是否不谐音,我们采用音高数字的绝对差值。例如,C和分别D#给出数字123,并且绝对差为9。实际的差为-9,实际的间隔为3(取-9模12)。但是由于上述对称性,我们可以考虑9代替3。由于9连续差向量中不存在音符,因此它们是辅音。


2
我喜欢在同一字符串中同时对音符和不谐音间隔进行编码的方式。
celtschk

8

JavaScript(ES6),68 64字节

以currying语法将注释当作两个字符串(a)(b)。返回0不谐调或1辅音。

a=>b=>488055>>(g=s=>'C D EF G A'.search(s[0])-!s[1])(a)-g(b)+9&1

测试用例

格式化和评论

a => b =>                       // given the two notes 'a' and 'b'
  488055 >>                     // 19-bit lookup bitmask: 1110111001001110111
    (g = s =>                   // we use g() to convert a note 's' into a semitone index
      'C D EF G A'.search(s[0]) // position of the note: -1 for 'B' (not found) to 9 for 'A'
      - !s[1]                   // subtract 1 semitone if the '#' is not there
    )(a)                        // compute the result for 'a'  --> [ -2 ...  9]
    - g(b)                      // subtract the result for 'b' --> [-11 ... 11]
    + 9                         // add 9                       --> [ -2 ... 20]
  & 1                           // test the bitmask at this position (0 if negative or > 18)

7

果冻,26 个字节

i@€ØAo.SḤ’d5ḅ4µ€ạ/“¢£©½¿‘ċ

单音链接,其中包含两个音符的列表(作为字符列表),然后返回0辅音和1不谐音。

在线尝试!或查看测试套件中的所有输入。

怎么样?

i@€ØAo.SḤ’d5ḅ4µ€ạ/“¢£©½¿‘ċ - Link: list of lists of characters, notes
              µ€           - for €ach note in notes: (call the resulting list x)
   ØA                      -   yield the uppercase alphabet
i@€                        -   first index of c in ^ for €ach character, c
                           -     ...note '#' is not there so yields 0 (A->1, B->2,...)
      .                    -   literal one half
     o                     -   or (vectorised)  - e.g. "C#" -> [3, 0] -> [3, 0.5]
       S                   -   sum
        Ḥ                  -   double - that is ...  C C#  D D#  E  F F#  G G#  A A#  B
                                                 ->  6  7  8  9 10 12 13 14 15  2  3  4
         ’                 -   decrement         ->  5  6  7  8  9 11 12 13 14  1  2  3
           5               -   literal five
          d                -   divmod                (e.g. 9 -> [1,4] or 11 -> [2,1])
             4             -   literal four
            ḅ              -   convert from base     (e.g. [1,4] -> 8 or [2,1] -> 9)
                                                 ->  4  5  6  7  8  9 10 11 12  1  2  3
                 /         - reduce x with:
                ạ          -   absolute difference   (e.g. ["G#", "A"] -> [12, 1] -> 11)
                  “¢£©½¿‘  - code-page indices = [1, 2, 6, 10, 11]
                         ċ - count occurrences (1 if in the list, 0 if not)

5

果冻,31个字节

O_65ị“¢[ḋṃ’b⁴¤+L$€Ḣ€ạ/e“cṾ’b12¤

在线尝试!

wheeeeee 32个字节太长

说明

O_65ị“¢[ḋṃ’b⁴¤+L$€Ḣ€ạ/e“cṾ’b12¤  Main link
O                                Cast each character to an int using Python `ord`
 _65                             Subtract 65 (A is 0, G is 7)
     “¢[ḋṃ’b⁴¤                   [2, 3, 5, 7, 9, 10, 0]
     “¢[ḋṃ’                      37058720
           b                     Digits in base
            ⁴                    16
    ị                            Index into this list; this creates the gaps for sharps
                 €               For each sublist
              +L$                Add the length to each element (Sharpens sharp notes)
              +                  Add
               L                 Length
                   €             For each sublist
                  Ḣ              Take the first element
                    ạ/           Absolute difference between the two (unoctaved) pitches # It's convenient that every interval's inverse (?) has the same consonance/dissonance
                      e          Is the semitone difference in
                       “cṾ’b12¤  [1, 2, 6, 10, 11]?
                       “cṾ’      25178
                           b     base
                            12   12

嘿,这是一个很好的答案!我想知道是否有人利用对称性,而您做到了。我也喜欢您的方法,以将音符名称与数字匹配!+1。
Ramillies

半音的差异可能是对称的,但您仍然会得到达芙效果- 例如,"G#", "A"(不和谐)产生的差异11不在中[1,2,6]
乔纳森·艾伦,

@JonathanAllan哦,那真令人尴尬;我以为绝对差异可以解决...._。将修复大声笑
HyperNeutrino

1
@JonathanAllan修复了几个额外的字节(3个IIRC)
HyperNeutrino

4

Mathematica,55个字节

function                                                  arguments        bytes

FreeQ[1|2|6|10|11]@Abs[#-#2&@@Sound`PitchToNumber/@#]&    [{"C","F#"}]     55

将内部内置映射到Sound`PitchToNumber输入(两个字符串的列表)上,取绝对差值,然后对不协调的间隔号进行模式匹配。


只是为了好玩(非竞争)

这里有一些较短的函数,它们违反了“您不得为音符分配任何其他名称”的限制。基本Music`包装具有预定义的音符常量(如A4 = 440.)和功能HertzToCents(可以打高尔夫球)。代替字符串,我们将使用音符常量作为参数,但是每个函数的格式都不同。

FreeQ[1|2|6|10|11]@Abs@@Round[.01HertzToCents@#]&         [{C3,Fsharp3}]   50+9=59
FreeQ[1|2|6|10|11]@Abs@Round[17Log[#2/#]]&                [C3,Fsharp3]     43+9=52
FreeQ[1|2|6|10|11]@Abs@Round[17Log@#]&                    [C3/Fsharp3]     39+9=48

包导入<<Music`;占用9个字节。

此函数将字符串(如"F#")转换为音符常量(如Fsharp3):

Symbol[StringReplace[#,"#"->"sharp"]<>"3"]&                                44

要接受大于八度的间隔,请替换Abs[…]Mod[…,12]


为什么有些间隔不和谐?间隔是两个频率的比率。如果比率具有“简单”的分子和分母,则它趋于更加辅音。在5-限制调谐,比可小于计入仅素数的整数幂或等于5。否间隔中相等的气质,除了八度,是一个只间隔 ; 它们只是使用2的12根的幂的近似值。

我们可以找到一个合理的间隔近似值,然后确定其分子和分母是否“简单”(这意味着分母小于5并且比率不除以7),而不是对间隔号进行不协调的硬编码。

下表显示了该过程中的每个步骤。

Table[
  Module[{compoundInterval,simpleInterval,rationalApprox,denomLeq5,div7,consonant},
    compoundInterval = Power[2, i/12];
    simpleInterval   = 2^Mod[Log2[compoundInterval], 1];
    rationalApprox   = Rationalize[N@simpleInterval, 1/17];
    denomLeq5        = Denominator[rationalApprox]<=5;
    div7             = Denominator[rationalApprox]>1 && rationalApprox\[Divides]7;
    consonant        = FreeQ[1|2|6|10|11][Mod[i,12]];

    InputForm/@{
      i, simpleInterval, rationalApprox, 
      denomLeq5, div7, denomLeq5 && !div7,
      consonant
    }
  ], {i, 0, 12}
]

i   sInterval  ratio   denomLeq5  div7       den&&!div  | consonant?

0   1          1       True       False      True       | True
1   2^(1/12)   17/16   False      False      False      | False
2   2^(1/6)    9/8     False      False      False      | False
3   2^(1/4)    6/5     True       False      True       | True
4   2^(1/3)    5/4     True       False      True       | True
5   2^(5/12)   4/3     True       False      True       | True
6   Sqrt[2]    7/5     True       True       False      | False
7   2^(7/12)   3/2     True       False      True       | True
8   2^(2/3)    8/5     True       False      True       | True
9   2^(3/4)    5/3     True       False      True       | True
10  2^(5/6)    7/4     True       True       False      | False
11  2^(11/12)  11/6    False      False      False      | False
12  1          1       True       False      True       | True

有理近似值位于1/17该区间内,因为这是区分所有12个相等回火区间的最大阈值。我们首先将有理数与模式Rational[a_,b_](或仅a_~_~b_匹配)匹配,然后将整数与only匹配_

这在以下相当短的函数中达到高潮,该函数确定任意频率比(大于1)是辅音还是不谐音。

Rationalize[#,1/17]/.{a_~_~b_:>b<=5&&!a∣7,_->True}&       [Fsharp3/C3]     51+9=60

1
天哪,不要告诉我有数学,即使是内置 ...:d
Ramillies

3

Mathematica,118个字节

FreeQ[{1,2,6,10,11},Min@Mod[Differences[Min@Position["C|C#|D|D#|E|F|F#|G|G#|A|A#|B"~StringSplit~"|",#]&/@{#,#2}],12]]&


输入表格

[“广告”]

产出

True->Consonant  
False->Dissonant   

谢谢@JonathanFrech -16个字节


请注意:您不需要输出字符串ConsonantDissonant。您可以输出任何两个值而不是它们(0/1,...等等)。这样可以节省一些字节。
拉米利斯

1
您可以省略If[...,0,1]和定义True->Consonant; False->Dissonant吗?
乔纳森·弗雷希

1
StringCases["CC#DD#EFF#GG#AA#B",_~~"#"...]– 42字节
celtschk

1
此外,2个字节可以通过更换被保存{1,2,6,10,11}1|2|6|10|11
celtschk

1
@Skyler请参阅下面的答案。
hftf

3

木炭,30字节

≔B#A#G#FE#D#C槔o∧⌈ς”⁻⌕ζ⮌θ⌕ζ⮌η

在线尝试!链接是详细版本的代码。输出1表示辅音,0表示不谐音。说明:

≔B#A#G#FE#D#Cζ                  Store reversed note names in z
                        θ       First input
                       ⮌        Reversed
                     ⌕ζ         Find index in z
                            η   Second input
                           ⮌    Reversed
                         ⌕ζ     Find index in z
                     ⁻          Subtract
               ”o∧⌈ς”           Compressed string 100111011100
              §                 Circularly index
                                Implicitly print

出于好奇,是否有一个助记符将字形⌕ζ用于“查找索引”?
乔纳

@Jonah ζ是分配给前面的变量。
尼尔

2

J,68个字节

[:e.&1 2 6 10 11[:(12| -~/)(<;._1',C,C#,D,D#,E,F,F#,G,G#,A,A#,B')i.]

说明

J中的一个简单但不是超级实现的实现:

  • 输入内容按框形式逐项列出(使用剪切产生)。

  • 在注释范围内找到它们的索引: (<;._1',C,C#,D,D#,E,F,F#,G,G#,A,A#,B')i.]

  • 从第二个减去第一个: -~/

  • 除以12所得的余数: 12|

  • 检查是否是不合逻辑的注释之一: e.&1 2 6 10 11

在线尝试!


2

///90 88字节

/^/"\///^\/\///C^D/##"E/DD"F/E#"G/FD"A/GD"B/AD"#,#/,"B#^B/#"A#/#"A^G#^G^F#/#"F^E^D#^D/#/

在线尝试!(一次所有测试用例)

  • 将输入内容放在代码之后。
  • ,B#在每个测试用例中,用分隔注释名称。
  • 输出,用于辅音,,#用于辅音。
  • 支持双重更改(##)或E#在某些特定情况下。否则,输出,为辅音,#,为非谐音(由于模12对称)
  • 可以一次处理多个测试用例(如果合理分开)
  • 小写字符将正确打印。

2

C(gcc),91字节

g(char*s){return (s[1]&1|2**s&15)*4/5;}f(char*x,char*y){return (1952220<<g(x)>>g(y))&2048;}

呼叫: f("A#", "D")

返回值:

  • 辅音:2048
  • 不和谐:0

奖励:该函数不区分大小写。

在线尝试!


两个return (s 中都没有两个不必要的空格吗?
乔纳森·弗雷希

您可以尝试一个g(char*s){s=(s[1]&1|2**s&15)*4/5;}f(char*x,char*y){x=1952220<<g(x)>>g(y)&2048;}很好的解决方案!
Keyu Gan'9

1

Python 2,125 117 83 78 77字节

a,b=map("C C#D D#E F F#G G#A A#B".index,input())
print chr(abs(a-b))in""

""在年底实际包含的字符"\x02\x04\x0c\x14\x16"

在线尝试!

(+3,因为我忘记了列表中的11或22)

乔纳森·弗雷奇(Jonathan Frech)的-8个字节,然后切换到Python 2

-34字节,包含来自Jonathan Frech的建议,并使用str的索引而不是list的。

从内联开始-4个字节i,Neil反转了字符串建议(实际上只有-2个,因为我忘记()了生成器)

取消内联i和更改输入格式的-5字节

来自Jonathan Frech的-1个字节,带有map()和不可打印。

在标准输入的一行中以以下格式输入:

'C','C#'

True是不和谐的,False是和声的。

旧说明:

i='C C#D D#E F F#G G#A A#B'.index
a,b=input()
print abs(i(a)-i(b))in[2,4,12,20]

Python str.index返回匹配子字符串的最低(正)起始索引,所以 "ABACABA".index("A") == 0"ABACABA".index("BA") == 1。因此,我们可以将音符名称均匀地放在字符串中,并且(例如)只要在字符串A之前A#,共享A就不会有问题。

i='C C#D D#E F F#G G#A A#B'.index

i现在是一个函数,该函数返回'C C#D D#E F F#G G#A A#B'其参数(音符名称)中的索引为2 *(音符从上升的半音数C

a,b=input()

Python 2 input()(在大多数情况下)与eval(input())Python3 等价,因此输入有效格式'C#','F'(例如),a='C#'并且b='F'

print abs(i(a)-i(b))in[2,4,12,20]

如果字符串中第一个音符和第二个音符之间的距离不是2、4、12或20(因为音符名称用2个字符表示),则间隔是不谐调的,输出True,否则是谐音的,打印False。


由于输入格式不严格,因此可以使用eval(input())(13字节)代替input().split()(15字节)。
乔纳森·弗雷希




1
您可以使用Unicode字符()代替空字符串。
乔纳森·弗雷希

1

C(gcc),115117 120 个字节

g(char*a){a=*a-65+!!a[1]*(7-*a/70-*a/67);}f(x,y)char*x,*y;{x="(pP$HL<lt<X"[g(x)]*32+"=ZukW-^h1F6"[g(x)]>>g(y)&1;}

在线尝试!

返回1/0表示谐音和反谐音。用纯C进行字符串操作总是很有趣。将输入作为f("A#", "C")


0

PowerShell,107字节

param($a,$b)[math]::abs(($x=-split'C C# D D# E F F# G G# A A# B').indexof($b)-$x.indexof($a))-in1,2,6,10,11

在线尝试!

辅音TrueFalse辅音的输出。

将输入$a和和$b两个注释作为字符串。-split按比例执行一个操作,该操作在空白处分割,以创建一个音符数组,并将其存储到中$x.indexof $b在该数组中找到,减去的索引$a,然后abs取其olute值。检查该数字是否-in为不协调范围。



0

SQL,582个字节

SQL小提琴

我仍然需要打高尔夫球,但是我想在完全弄破之前先把它打下来。

如果输入是字母格式,那么可以将这些字母放入带有值的表中,对吗?

CREATE TABLE N(N char(2),v int)
Insert Into N values('A',1),('A#',2),('B',3),('C',4),('C#',5),('D',6),('D#',7),('E',8),('F',9),('F#',10),('G',11),('G#',12);
CREATE TABLE D(D char(9),v int) 
Insert Into D values('C',0),('D',1),('D',2),('C',3),('C',4),('C',5),('D',6);
CREATE FUNCTION I(@A char(2),@B char(2))
RETURNS char(9) as
BEGIN
DECLARE @E int=(SELECT v from N where n=@A),@F int=(SELECT v from N where n=@B)
DECLARE @C char(9) = (SELECT case D when 'D' then 'Dissonant' when 'C' then 'Consonant' END from D where v in(abs(@e-@f),12-abs(@e-@f)))
RETURN isnull(@C,'NotANote')
END

0

Perl 5,106个字节

("C,C#,D,D#,E,F,F#,G,G#,A,A#,B,"x2)=~/$F[0],(.*?)$F[1],/;$r=(1+($1=~y/,//))%12;say(grep/$r/,(0,3..5,7..9))

在线尝试!

对于不谐调,返回false;对于辅音,返回true。

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.