北经北经东南


30

给定一串N,S,E和W,输出方位角(以度为单位从北方顺时针旋转的角度),精确到小数点后5位。

传统的指南针表示法中,字符串仅由其中两个字符组成(例如NNW或ESE)。在这里,您还必须接受包含所有4个字符串(例如WNNNSE)。仅使用2个符号即可使人们直观地理解其含义。允许使用4个符号使阅读变得很恐怖,但是允许以较短的方式描述给定精度的轴承。

(正如user2357112注释中指出的那样,事实证明您可以证明对于任何给定方位,4符号字符串的长度将与2符号字符串的长度完全相同,因此我将这一挑战基于错误的假设。希望这种缺乏实际用途不会损害您对挑战的享受...)

确切的方法如下所述,并且等效于传统的表示法(它在其上进行扩展而不是对其进行更改)。

输入项

  • 输入是仅包含字符的单个字符串NESW
  • 如果您愿意,输入可以是一个字符序列,前提是该输入不包含任何预处理。例如,[N, [E, [S, [W]]]]不允许使用嵌套列表来帮助处理顺序。
  • 不允许使用其他字符。您不能使用1234代替的字符串NESW

输出量

  • 输出必须是十进制数字或一的字符串表示形式(不是有理数/分数)。
  • 不需要显示尾随零。如果方位角是9.00000,则输出9也算作正确的小数点后5位。
  • 输出范围为[0,360)。也就是说,包括0但不包括360。
  • 通过将输出四舍五入到小数点后五位来检查正确性。如果轴承为0.000005,则四舍五入为0.00001。输出0.00001和0.000005都是正确的。
  • 对于某些输入,以科学计数法表示的输出是可以接受的。例如,1e-5代替0.00001

转换次数

  • 单字符罗盘点NES,和W分别对应于0,90,180,以及270度。
  • 将其中之一添加到字符串会导致方位将单个字符的方位和原始弦的方位一分为二。
  • 选择两个可能的平分轴承中最接近的一个,以便NE代表45度,而不是225度。
  • 除了要平分的角度为180度的地方,这是明确的。因此NSSNWE,和EW对应于未定义的轴承,以及输入将永远不会在任何这些结束。但是,它们可能会出现在输入字符串的任何其他位置,因为这不会引起歧义。
  • 如果最后两个字符相同,则最后一个字符将是多余的,因为二等分将返回相同的方位。由于这没有为表示法添加任何内容,因此您的代码无需处理。因此NNEESS,和WW对应于未定义的轴承,以及输入将永远不会在任何这些结束。但是,它们可能会出现在输入字符串的其他任何地方。

例子

N: 0
E: 90
S: 180
SE: halfway between S and E: 135
NSE: halfway between N and SE: 67.5
NNSE: halfway between N and NSE: 33.75
NNNSE: halfway between N and NNSE: 16.875
NNNNSE: halfway between N and NNNSE: 8.4375

测试用例

提交仅在给出所有测试用例的正确输出时才有效。请注意,测试用例达到了可以双精度处理的极限。对于默认为单精度的语言,您可能需要花费字节来指定双精度,以获取正确的输出。

测试用例输出显示为四舍五入到小数点后五位,并且四舍五入为任意精度。两者都是有效的输出。

WNE 337.5 337.5
WEN 337.5 337.5
WEWEWEWEWEWEWEWEWEWEWEN 330.00001 330.000007152557373046875
NESWNESWNESWNESWNESWNESWNESW 90 89.99999932944774627685546875
NNNNNNNNNNNNNNNNNNNNNNNE 0.00001 0.0000107288360595703125
NNNNNNNNNNNNNNNNNNNNNNNW 359.99999 359.9999892711639404296875
SNNNNNNNNNNNNNNNNNNNNNNNE 90.00001 90.00000536441802978515625
SNNNNNNNNNNNNNNNNNNNNNNNW 269.99999 269.99999463558197021484375

计分

这是。分数是源代码的长度(以字节为单位),最短者获胜。


脚踏车

我误以为“西北偏北”是正确的指南针方向。一个错误的错误,因为它导致了一个挑战性的构想,但后来我在Wikipedia页面上发现:

“ Alfred Hitchcock 1959电影的标题,西北偏北,实际上不是32风指南针的方向点,而是电影中提到了西北航空公司。”

事实证明,用于此挑战的方法仅与传统的罗盘指针一致,直到16点罗盘为止。该页面上描述的32风罗盘略有不同,我很容易忽略了它的存在以应对这一挑战。

最后,对于任何认为我应该使用“东南”而不是“东南”的人,


WNNNSE<=在您的帖子开始时,此示例条目的输出是什么?这对我来说听起来是无效的,但这很难说。
Tensibai '16

@Tensibai对于输入WNNNSE,输出为323.4375。请参阅示例部分,以获取与本案例相同的演练。
trichoplax

输入是否f(N,N,N,S,E)还好?
Karl Napf '16

@KarlNapf我扩展了输入部分以进行澄清。如果我理解正确,那么带有多个参数的示例输入似乎等效于一个字符序列,因此可以接受。
trichoplax

2
“允许使用4个符号使阅读变得很恐怖,但是允许以较短的方式描述给定精度的轴承。” - 你确定吗?似乎所有描述同一方位角的输入都具有相同的长度,因为如果您为每个方位角分配从0到1的二进位有理数,则长度N的N> 1的字符串总是与分母2 ^(N +1)。另外,在方位中允许两个以上的不同字母不会增加表达力;具有3个或4个字母表示的任何轴承可以用2来表示
user2357112支撑莫妮卡

Answers:


13

JavaScript(ES6),84 80 78 74 72字节

@Titus节省了一个字节,@ Neil节省了一个字节

f=([c,...s],b="NESW".search(c))=>b*90-(s[0]?(b-=f(s)/90)-4*(b*b>4):0)*45

花了一段时间,但我想我终于完善了公式...

测试片段

说明

让我们从最简单的情况开始:一个单字符字符串。结果就是它在字符串中的位置(0索引)NESW乘以90。

对于两个字符的字符串,结果位于第一个字符的结果与第二个字符的结果之间的一半。但是,有一个陷阱:如果两者之间的绝对差大于180(例如NWWN),则必须将角度设为180,以使其不会指向相反的方向。

对于任何更长的字符串,结果都位于第一个字符的结果与其余字符串的结果之间的一半。可以通过以下方式对此进行概括:

  • 如果输入是单个字符,则在字符串中NESW乘以90 返回其索引。
  • 否则,返回字符串中第一个char的索引NESW乘以45,再加上字符串其余部分的一半;如果两者之间的绝对差值大于90,则增加180。

切断字符串中第一个字符的绝佳方法!如果将值除以45,则可以节省一个字节
。– Titus

@Titus我可以用这种技术节省2个字节,谢谢!
ETHproductions '16

1
search而不是indexOf节省您一个字节。
尼尔

@尼尔再次感谢!通过完全重新安排方程式,我设法再打三球。
ETHproductions 2016年

10

C#6,226个 217 207 185字节

using System.Linq;double N(string s){double b=f(s.Last());foreach(var c in s.Reverse()){b=(b+f(c)+(b-f(c)>2?4:f(c)-b>2?-4:0))/2;b=(b+4)%4;}return b*90;}int f(char x)=>"NESW".IndexOf(x);

编辑:从ETHproductions提交的“借用”想法中获得-10个字节
-22个字节,这要归功于@Titus

不打高尔夫球

// Call this method
double N(string s){
    // Initialize bearing with last direction
    double b=f(s.Last());
    // Do backward. Doing last direction once more doesn't impact result
    foreach(var c in s.Reverse()){
        // Average current bearing with new bearing, adjusted with wrapping
        b=(b+f(c)+(b-f(c)>2?4:f(c)-b>2?-4:0))/2;
        // Make bearing back to range [0,4)
        b=(b+4)%4;
    }
    // Change from "full circle = 4" unit to degree
    return b*90;
}
// helper method to convert direction to bearing. This returns bearing with full circle = 4.
int f(char x)=>"NESW".IndexOf(x);

我认为您可以使用%使范围调整缩短至[0,360)
trichoplax

@trichoplax会减少小数位数吗?
泰特斯


1
使用b=(b+360)%360;代替保存10个字节b+=b>360?-360:b<0?360:0;。通过将所有内容除以90和来节省另外12个字节return b*90;
泰特斯(Titus)

1
这里还有10个字节:合并两个分配并删除花括号:b=(b+f(c)+(b-f(c)>2?4:f(c)-b>2?-4:0)+8)/2%4;然后分配+8给三元结果b=(b+f(c)+(b-f(c)>2?12:f(c)-b>2?4:8))/2%4;
Titus

8

PHP,95 88 86 100 127 104 101字节

  • 空合并运算符为-7个字节
  • 不替换-2个字节N(还有更多个,因为这样可以将转换放到循环头:N确实如此,但是0在计算中求和。)
  • +41个字节用于平分(咳嗽
  • @ETHproductions代码直接启发了-7个字节和-16个间接字节
  • strtr我的一些杂耍之一替换-3个字节

for($i=strlen($s=$argv[1]);$i--;$p=($q+$p=$p??$q)/2+2*(abs($q-$p)>2))$q=ord($s[$i])/.8+3&3;echo$p*90;

这是我第一次正式使用null合并运算符。用运行-r

PHP 7.1

在即将到来的PHP版本的负极串偏移量将节省12个字节:
替换strlen($s=$argv[1])0$s$argv[1]


(几乎)每个人的可用字节:

  • 用0、1、2、3而不是0,90,180,270进行计算,并将最终结果乘以90将节省两个字节 并且可能允许进一步打高尔夫球。
  • 字符的ASCII码中有一些模式。尝试使用您的语言之一:
    • (a/2%6+2)%5
    • a<87?a/2&3^3:3 要么 a/2&3^3*(a<87)
    • a&1?a&2|a/4&1:0
    • a/.8-1&3

5

Python 3, 133个 113字节

只是改进@ L3viathan的答案,因为我刚刚创建了此帐户,因此无法发表评论。

d={"N":0,"E":.5,"S":1,"W":1.5}
def B(s):
 b=d[s[-1]]
 for c in s[::-1]:b=(b+d[c])/2+(abs(b-d[c])>1)
 return b*180

欢迎使用编程拼图和代码高尔夫球,还有很棒的改进...
trichoplax

我没有看到您的答案,但泰特斯(Titus)有一个类似的主意,再加上我有一个主意,我现在降至98 :)
L3viathan

5

05AB1E48 42 37 32字节

感谢Emigna,节省了6个字节。由于提多斯(Titus)的想法在[0,4 [范围内工作,最后乘以90,所以节省了5个字节。由于Adnan对古代异或模态的掌握,节省了5个字节。

因此,在整个执行过程中,每个角度都从[0,360 []范围减小到[0,4 []范围。然后将结果乘以90并显示。

Ç30^5%R¬U¦vXy+;DX-Ä0›2*+4%U}X90*

It can be divided into two sequentially called subprograms.
First program: convert input string into an array of the corresponding angles in range [0,4[
Ç      Take the ascii value of all input characters
 30^5% Dark ascii manipulation that yields [0,1,2,3] for [N,E,S,W]

Now we have an array of integers in range [0,4[.

Second program: actually compute the final angle
R                          Reverse the array
 ¬                         Take the first value (the last of the non-reversed array)
  U                        Pop it from the stack and set X to the same value
   ¦                       Strip the first element
    v                      For each remaining element
     Xy+;                  Compute the average value between the leftmost value and X
         DX-Ä0›            Push 1 if angular distance cast to integer is > 0 (i.e. if it is >= 1), 0 otherwise. It's equivalent to checking >= 90 degrees
               2*+         Multiply by 2 (=2 if angular distance is >= 1 and 0 otherwise) and add it to the formerly computed average value. It's equivalent to multiplying by 180
                  4%       Perform mod 4. It's equivalent to performing mod 360
                    U      Store the result back to X
                     }     End for, mandatory if input has only one character
                      X90* Push X*90 and implicitly display it

在线尝试!

打高尔夫球的可能轴:

  • 不确定是否需要mod 4(它将节省2个字节)。没有它,所有测试用例都可以工作,但是也许存在一个棘手的用例。验证或使之无效的数学证明将是一流的。
  • 除了显示结果外,没有其他隐式内容(右引号,右括号)。

1
似乎并没有在NNNNNNNNNNNNNNNNNNNNNNNESNNNNNNNNNNNNNNNNNNNNNNNE测试用例上提供请求的结果。
Emigna '16

2
奇怪的。现在我也做。对不起,我必须粘贴错了什么。您可以将代码缩短为v"NESW"yk90*})R¬U¦vXy+;DX-Ä89›180*+360%U}X
Emigna '16

1
很好的解释!值得一提的是,89›实际上意味着整数部分大于89,这相当于说整数大于或等于 90(这仍然可以正常工作,因为绝对不应出现90)。目前,所解释的代码中的注释听起来好像正在检查是否大于89,而您的代码通过了测试用例,因此显然可以正确地检查是否大于
90。– trichoplax

1
我对说明进行了编辑,但由于不确定操作符对负浮点值的行为,因此写了“ cast to integer” 。这里没有问题,因为它适用于绝对值,但是我宁愿不要对运算符做过多的假设。
2016年

1
您可以替换v"NESW"yk})Ç30^5%:)
Adnan

5

Python 3、146 145 117 107 97 94 93 92字节

f(s):u='NESW'.find(s[0])*90;return(u+f(s[1:]))/2+180*(abs(u-‌​f(s[1:]))>180)if s[1:]else u

f用字符串调用。


您不能让...0else它们两个抛出SyntaxErrors。
乔纳森·艾伦

@JonathanAllan您正在使用什么版本的Python?我在3.5.2上,它可以工作。
L3viathan

我跑了它在3.3.3 -你可以删除之间的空间else-太多?(可以在3.3.3中使用)
乔纳森·艾伦

@JonathanAllan是的,我可以!谢谢,这为我节省了另一个字节。
L3viathan

2
@Titus d.find可以,我在一分钟前有了确切的主意;查看最新答案。
L3viathan

5

C,184字节

double h(char c){return ((c=='E')+(c=='S')*2+(c=='W')*3);}double d(char*s){double f=h(*s);if(s[1]){double t=f;f=(f+d(s+1)/90)/2;if(((t-f)>1)||((f-t)>1))f+=2;if(f>=4)f-=4;}return f*90;}

不打高尔夫球

// a helper function
double direction_(char ch)
{
    if (ch=='N')
        return 0.;
    else if (ch=='E')
        return 90.;
    else if (ch=='S')
        return 180.;
    else
        return 270.;
}

// this is the main function to call
double direction(char* str)
{
    double fAngle = direction_(str[0]);
    if (str[1])
    {
        double tmp = fAngle + direction(str+1);
        if (tmp>=360.)
            tmp-=360.;
        tmp/=2;

        if (((tmp-fAngle)>90.) || ((tmp-fAngle)<-90.))
        { //  check if we need to take the "other side"; if the resulting angle is more than 90 degrees away, we took the wrong on
            if (tmp>=180.)
                tmp-=180.;
            else
                tmp+=180.;
        }
        fAngle = tmp;
    }
    return fAngle;
}

似乎使用float并不能为您提供所需的精度。
Eyal Lev

4
欢迎来到PPCG!:D
mbomb007 '16

函数的名称是否会彼此冲突(因为名称都是d)?
clismique

@qwerp,不同的签名(一个带字符*,另一个带字符)
Eyal Lev

2
函数名不会缺胳膊少腿的名字在C,因为它们是在C ++中,因此你需要重新命名他们中的一个,如果你希望它是C.
克拉斯Lindbäck

3

R,172146字节

z=rev((0:3*90)[match(scan(,""),c("N","E","S","W"))]);p=z[1];l=length(z);for(i in 2:l)p=(p+z[i])/2+(abs(p-z[i])>180)*180;if(l<2)p=z;sprintf("%f",p)

不打高尔夫球

z=rev((0:3*90)[match(scan,""),c("N","E","S","W"))]); #1
p=z[1];                                              #2
l=length(z)                                          #3
for(i in 2:l)p=(p+z[i])/2+(abs(p-z[i])>180)*180;     #4
if(l<2)p=z                                           #5
sprintf("%f",p)                                      #6

讲解

  1. 从stdin读取输入
    • 将输入按索引匹配到 c("N","E","S","W")
    • 从匹配的索引中:匹配度数的向量0:3*90(而不是c(0,90,180,270)
    • 反转并存储为 z
  2. 初始化p为等效于输入中最后一个字符的程度
  3. 将输入长度存储为 l
  4. 迭代地,计算两个可能的平分轴承中最接近的一个。
  5. 如果仅给出一个输入,则设置pz
  6. 格式化和打印

在R-fiddle上尝试测试用例 (请注意,这是由于scan无法在R-fiddle上使用而导致的功能)


只要输出正确到小数点后5位,就不需要进行舍入。来自挑战:Outputs 0.00001 and 0.000005 are both correct.因此,您应该能够通过
不舍

@trichoplax我了解。输入也可以是像这样的字符串字符的向量。c("N","N","E")代替"NNE"?这相当于一个非嵌套的python list ["N","N","E"]
Billywob

是。我打算将“序列”作为一个通用术语,包括数组,向量,列表,元组之类的东西。
trichoplax

1
我认为如果将所有内容除以90并用printf(p * 90)可以节省4个字节。
泰特斯(Titus)

3

Haskell,109105103字节

h=180
a#b|abs(a-b)<h=n|n>h=n-h|1>0=n+h where n=(a+b)/2 -- calculates the new "mean" on the cirlce
f 'N'=0                                          -- translates characters to angles
f 'E'=90
f 'S'=h
f _=270
foldr1(#).map f                                  -- traverses the whole string

感谢-2字节@xnor!


详尽的清单f看起来很长,但是我很难找到更短的清单。我最近得到的是f c=90*until(\i->"NESW"!!i==c)(+1)0(35)。我认为您可以替换'W'_
xnor

是的,我也希望它会更短一些,但是什么也没找到。谢谢_
瑕疵的

3

Dyalog APL55岁 45 38 字节

要求⎕IO←0,这在许多系统上都是默认设置。要求方向。

360|÷○÷180×12○(+÷(|+))/¯12○○2÷⍨'NES'⍳⍞

说明

由每个字母转换成一个复数去解决该问题1∠ θ一个 + B?我,然后做从右到左的总和减少(APL的长处),而在归一化每一个步骤。然后将最终的θ转换为度数并归一化为[0,360)以内:

'NES'⍳⍞每个输入字母的索引以“ NES”为单位;N→0,E→1,S→2,其他→3

○2÷⍨转换为弧度的角度;θ = π · x 2

¯12○在单位圆上转换为复数;e i·θ

(...用... )/减少列表(即在...的元素之间插入函数)

+÷(|+)...归一化的总和;x n -1 + x n| x n -1 + x n |

12○转换为角度 θ

÷○÷180×转换为度;1 / π · 1 / 180· X

360| 除数除以360

在线尝试APL!

轶事

如果输入和输出是正交复数单位,则整个解决方案将是:

(+÷(|+))/

其余代码将解析输入和格式化输出。


我注意到测试输出与挑战中的输出不匹配,保留到小数点后5位,这使其无效。Dyalog APL是否可以选择使用双精度?
trichoplax

@trichoplax是的,⎕FR←1287使用了128位浮点数,但是TryAPL不允许使用它。
阿达姆,2013年

我认为大于或等于64位浮点数的任何东西都应该起作用(不过我仅在Python中进行了测试)。这是否意味着您可以使代码有效,但仅对安装该语言的用户有效?也许您可以显示分数的完整有效代码,并包括不太准确的在线版本,以便人们看到算法是正确的。
trichoplax

@trichoplax实际上,TryAPL使用双精度,但是您的测试用例累积的错误超过53位。
2013年

如果可以证明差异是由于IEEE 754标准的解释上的差异(仍然符合标准),那么我将调整测试用例以确保两种解释将相同的结果赋予5位小数位。我选择了测试用例,以使它们对于浮点数(双精度)和任意精度的小数都将相同的结果赋予5个小数位。我来看看。
trichoplax

2

通用Lisp,347327字节

感谢@Titus的帮助

这可能可以打更多的球,但是至少它起作用(我认为):

(defun d(c)(if(eql c #\N)0(if(eql c #\E)1(if(eql c #\S)2(if(eql c #\W)3)))))(defun m(a b)(if(> a b)(rotatef a b))(if(<(+(- 4 b)a)(- b a))(+(/(+(- 4 b)a)2)b)(+(/(- b a)2)a)))(defun f(s)(let((c))(setf c(d(char s(1-(length s)))))(do((a)(p(-(length s)2)(1- p)))((< p 0))(setf a(char s p))(setf c(m(d a)c)))(format t"~5$"(* c 90))))

用法:

* (f "WNE")
337.50000
NIL

功能 d需要一个字符NEW,或者S返回相应的学位。功能m获得两个给定方向的适当组合度。函数f迭代提供的字符串,计算适当的度数,并将其打印为浮点数。


我的LISP生锈了,但是除以90可以节省6个字节吗?
泰特斯(Titus)

@Titus我想会的。我意识到了一些其他方面的改进,所以我会添加这个当我在我的电脑
artificialnull

2

Befunge, 183 181 175字节

>~#+:#25#%6*#/`#2_$>5%4*:00p"Z}"4*:***20g#v_+2/00g10g-:8`\0\-8`+!v
v5:+*:*:"d"/+55+5$_^#!:\p01/**:*4"}Z":p020<%**:*"(2Z"+**5*:*"0}"!<
>5>+#<%#56#58#:*#/+\#5:#5_$$$,,,".">:#,_@

在线尝试!

说明

这与许多其他答案遵循类似的算法,只是因为Befunge不支持浮点,所以它使用的是用整数模拟的定点计算。

感谢@Titus提供ASCII到int例程。

 ~ : 5 6* ` _$        while ((c = getchar()) > 30)  // ends with any ctrl char or EOF
> + 2 %6 / 2            push(c / 2 % 6 + 2)         // partial conversion to int

                      do {
  5%                    dir = pop() % 5             // completes the conversion to int   
  4*:00p                dir *= 4; lowres_dir = dir  // used by the 180-flip calculation
  "Z}"4*:***            dir *= 22500000             // this is 90000000 / 4 
  20g_                  if (!first_pass) {
    +2/                   dir = (dir+last_dir)/2    // last_dir is second item on stack
    00g10g-               diff = lowres_dir - last_lowres_dir
    :8`\0\-8`+!!          flip = diff>8 || -diff>8
    "}0"*:*5**+           dir += flip * 180000000   // add 180 degrees if we need to flip
    "Z2("*:**%            dir %= 360000000          // keep within the 360 degree range
                        }
  020p                  first_pass = false
  :"Z}"4*:**/10p        last_lowres_dir = dir / 22500000
  \                     last_dir = dir              // saved as second item on stack
  :!_                 } while (!stack.empty())

$                     pop()                         // this leaves the final dir on top
5+55+/                dir = (dir + 5)/10            // round down to 5 decimal places
"d":*:*+              dir += 100000000              // add a terminating digit
                      while (true) {                // convert into chars on stack
:55 + % 6 8 * +\ : _    push(dir%10+'0'); if (!dir) break
   > < 5 5 : /+ 5 5     dir /= 10
                      }

$$$                   pop() x 3                     // drop the chars we don't need
,,,                   putchar(pop()) x 3            // output first three chars
"."                   push('.')                     // add a decimal point
>:#,_@                while(c=pop()) putchar(c)     // output the remaining chars

这是否仅意味着您需要模拟更大的定点类型(更多小数位)?测试用例设计为需要双精度,即不超过17个有效数字(最多16个小数位),而14个小数位可能就足够了。
trichoplax

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.