我正在尝试从Dragon Curve实现修改后的AoC Day 16作为Haskell中的无限列表。
该列表由True
和False
组成。我们从一些列表s0
开始:
s1 = s0 ++ [False] ++ (map not . reverse) s0
s2 = s1 ++ [False] ++ (map not . reverse) s1
s3 = s2 ++ [False] ++ (map not . reverse) s2
一般
sn = s(n-1) ++ [0] ++ (map not . reverse) s(n-1)
= s0 ++ [0] ++ (f s0) ++ [0] ++ (f (s0 ++ [0] ++ (f s0))) ++ ...
where f = (map not . reverse)
我可以使用sn
函数轻松获得iterate
。
modifiedDragonCurve :: [Bool] -> Int -> [Bool]
modifiedDragonCurve s n = (iterate f s)!!n
where f s = s ++ [False] ++ (map not . reverse) s
这给了我一个列表[s0, s1, s2, ...]
。但是,由于s(n-1)
是sn
的前缀,因此可以将其构建为无限列表,但我无法弄清楚如何处理它。
modifiedDragonCurve :: [Bool] -> [Bool]
modifiedDragonCurve s = s ++ [False] ++ (map not . reverse) listSoFar
但无法弄清楚如何引用已生成的列表(listSoFar
)。
任何建议都将不胜感激。
答案 0 :(得分:5)
我也在解决AoC问题的同时自己玩这个。我找到了一个不需要反向的卓越解决方案,因此比这里列出的其他解决方案更加内存友好和快速。它也很漂亮!龙曲线本身是一个很好的短双线:
merge (x:xs) ys = x:merge ys xs
dragon = merge (cycle [False, True]) dragon
它可以扩展为使用“种子”,因为AoC问题只需要在种子和真龙曲线的位之间交替:
infinite bs = go bs (map not (reverse bs)) dragon where
go bs bs' (d:ds) = bs ++ [d] ++ go bs' bs ds
(这确实会调用reverse
一次 - 但与其他解决方案不同的是,它只在一大堆关于输入大小的数据上调用一次,而不是在与该部分一样大的数据块上重复调用你消费的清单。)一些时间来证明我的主张;用于生成具有空种子的2 ^ 25个元素的所有版本,使用ghc -O2
编译,并使用/usr/bin/time
计时。
自由泳的解决方案需要11.64s,最多1.8Gb驻留 David Fletcher的解决方案需要10.71s,最大2Gb驻留 luqui的解决方案需要9.93秒,最大居民约1GB 我的解决方案需要8.87秒,最大居民约760MB
完整的测试程序
main = mapM_ print . take (2^25) . dragon $ []
将dragon
依次替换为每个实现。精心设计的消费者可以进一步降低内存使用率:到目前为止,我对第二个星问题的最佳解决方案是以5Mb实际驻留时间运行(即包括从OS分配的多代GHC空间和其他RTS开销的所有空间GHC) ),60Kb GHC报告的驻留时间(即只有尚未GC的对象使用的空间,无论GHC从操作系统分配了多少空间)。
但是对于原始速度,你无法击败Bool
的未装箱的可变矢量:同事报告说他的程序运行时间为0.2秒,使用大约35Mb内存来存储完整的扩展(但不是无限!)矢量。
答案 1 :(得分:2)
这是一种方式。我们列出的不是s0,s1等,而是每一步只有新的部分,然后我们可以将它们连在一起。
dragonCurve :: [Bool]
dragonCurve = concatMap f [0..]
where
f n = False : (map not . reverse) (take (2^n-1) dragonCurve)
(这假定s0 = []。如果它可能是其他东西你将不得不修改长度计算。)
我无法想到一种既可以自我指导又不处理前缀长度的好方法。这是一个非自引用解决方案,仍然使用制作非重叠部分列表的想法。
dragonCurve' :: [Bool]
dragonCurve' = concat (unfoldr f [])
where
f soFar = Just (part, soFar ++ part)
where
part = False : (map not . reverse) soFar
答案 2 :(得分:2)
你完全是书呆子用这个来嘲笑我。它不是一个自我引用的列表,但我确实设法提出了一个“无用的”#34;解决方案 - 我们不会丢弃或忘记我们计算过的任何事情。
dragon :: [Bool] -> [Bool]
dragon = \s -> s ++ gen [] s
where
inv = map not . reverse
gen a b =
let b' = inv b
r = b' ++ a
in False:r ++ gen r (False:r)
gen a b
接受当前序列的所有数据作为输入,使得当前序列为inv a ++ b
。然后我们在r
中生成余数,输出它并递归地继续生成余数。我接受a
倒置,因为我需要做的就是在每个步骤(甚至不检查b'
)前加a
,而且我们不需要反转超过DList
我们必须。
作为nerdsniped我调查了很多其他数据结构,想象链接列表可能不是最适合这个问题,包括Data.Sequence
, <?php
/*
Attempt MySQL server connection. Assuming you are running MySQL
server with default setting (user 'root' with no password)
*/
$link = mysqli_connect("localhost", "***", "***", "***");
// Check connection
if($link === false){
die("ERROR: Could not connect. " . mysqli_connect_error());
}
// Escape user inputs for security
$cname = mysqli_real_escape_string($link, $_POST['cname']);
$name = mysqli_real_escape_string($link, $_POST['name']);
$contact = mysqli_real_escape_string($link, $_POST['contact']);
$reason = mysqli_real_escape_string($link, $_POST['reason']);
// attempt insert query execution
$sql = "INSERT INTO keys (cname, name, contact, reason) VALUES ('$cname', '$name', '$contact', '$reason')";
if(mysqli_query($link, $sql)){
echo "<script>alert('User added!')</script>";
echo "<script>window.open('index.php','_self')</script>";
} else{
echo "ERROR: Could not able to execute $sql. " . mysqli_error($link);
}
// close connection
mysqli_close($link);
?>
(手指树),自由monoid(它应该擅长被逆转),以及取消逆转的自定义树。令我惊讶的是,列表仍然表现最好的所有这些,我仍然对此感到困惑。万一你好奇,here is my code。
答案 3 :(得分:1)
例如:
dragon s0 = s0 ++ concatMap ((False :) . inv) seq
where
inv = map not . reverse
seq = iterate (\s -> s ++ False : inv s) s0