duangsuse::Echo
718 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
这个应该是自然语言处理和机器学习方面的可视化
duangsuse::Echo
这个应该是自然语言处理和机器学习方面的可视化
This media is not supported in your browser
VIEW IN TELEGRAM
说起来,弄了半天也没写半个字的实际内容,全搞排版测试什么的去了....
duangsuse::Echo
这个应该是自然语言处理和机器学习方面的可视化
所以说大佬实在不少,哭哭(但其实是好事... 大佬对社会都是“超值”的,就是我们这些菜鸡看了会自闭而已)
This media is not supported in your browser
VIEW IN TELEGRAM
可能是我这几个星期熬过最长的夜了...

虽然还是啥都没有做到
不过至少还是有努力过不后悔吧...(虽然肯定会很后悔为什么不等下个星期搞

虽然又是啥都没有完成,可是也完成了了一些东西呢。
#life
duangsuse::Echo
#Rust #PLT https://lexuge.github.io/ #recommended #blog
全英文
5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前

我... 其实我一直在想的是(没错,我记错了,但是这句话我一直在想一直在想... 我真的不知道到底是哪里,在设计什么算法什么数据结构才可能导致他理解出了这类偏差)

a 没有被手动排序过,a 排在 c 的前面,b 被手动排序过,b 也在 c 的前面,但是 a 和 b 却无法确定谁在前面

之前一直以为是抽象的,实际上居然是用数值... 那我应该又找到新的突破点了...

之前我一直怀疑是不是他修改那类算法的时候出了问题,现在想想也可能是调试的时候发现了这个『漏洞』

是不是也有可能是把“不可排序”的文章映射到数值的时候导致的问题?
如果后端存储是支持查询排序的 ORM,是不是可以说就是上面这种情况?

我猜的第一种情况是对于任意两项

import java.util.function.*;

class OrderLT<T> implements Comparator<T> {
private enum Ord { LT, EQ, GT; }
private final Predicate<T> lessThanP;
public OrderLT(Predicate<Pair<? super T, ? super T>> ltP) {lessThanP = ltP;}

@Override int compare(T a, T b) {
if (lessThanP.test(Pair.of(a, b))) return LT.toInt();
return (a.equals(b)? EQ : GT).toInt();
}

@Override boolean equals(Object cmpQ) {return cmpQ instanceof OrderLT && cmpQ.lessThan}
}

上面这个是 Ord 的实现,但是我不该强行用 Predicate... 有点过分了...
我想想可能是什么问题...

我之前想到的第一个是为每个文章建立一个『是否被手动排序过?』布耳值,对于表中每(要比较的)两项,如果任何一项被标记为手动排序过则直接返回预期的顺序(默认按照 lessThan 排序,输出升序列 Ascending sequence,这种情况直接返回 -1 (LT)表示顺序已经满足)不过,虽然理论上会导致这种问题(这是肯定的,原因我待会的文章会说)但是实践上(假设是冒泡排序的交换,快速排序就不能肯定了)只会导致手动排序部分以及边上的两个被“锁死”不可能移动位置而已,没有 5,4 『找不到』谁在前的问题(显然 5 旁边锁死了排序算法就不会工作,因为两项的顺序满足了目标序列,如果本来就是正确的则自动部分排序结果正确,否则自动排序部分是非有序列的,但对此算法是正常情况)。

当然排序算法按操作分选择、插入、交换三类,序的理论却是唯一的,就一个 lessThan 和一个反对称性、传递性最重要(如果不是 lessThan 显然就是 equals 或者 greaterThan 了,不过实际上实现这一个 predicate,判定[1])

5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前

Given
list <= {5,4,1} (>)
Where
5 自动排序
5 在 1 前面
4 在 1 前面
In
5 和 4 无法得出谁在谁前

其中有一个容易误会的点是对于集合 {5,4,1} (>) 序是指算法输出

forall i in lastIndex(list). list[i] > list[i+1]

(当然这里我不指定 i in... 或者随便指定 i in indices(list) 也没错... 数学嘛,请滑稽处理,跑)

而不是『某简单排序算法看到 5 > 4 为真,则交换成 4,5, 它是“确保”条件成立并且应用偏序传递性而不是“否认”比较操作符』

... 首先根据 5 和 4 无法得出谁在谁前... 认为是在设计排序算法的过程中出的问题吧
因为 drakeet 是一个非函数式也不太学算法的(真正意义上的纯)工程师,假设他使用了把“文章”直接映射到某标量的方式,我觉得比较有可能(而不是比如设置一个 lessThan 关系集合,然后 comparator 判断某项是否在集合内并且打破偏序)

.... 这种算法到底是哪样的呢?假设它是类似冒泡排序的交换排序

bubbleSort (>) [5,4,1]

它要比较两次:

5>4
= true
4>1 = true

可显然这不是问题,那就假设我之前的猜测是对的,输入是一个二元组

bubbleSort cmp [(4, True), (1, True), (5, False)]
where cmp (x, p) (r, q) = if p and q then (fst x) > (fst r) else True)

我假设他所说这个『无法得出』是无法得出“正确”的结果的意思,那这个程序的输出是(product 箭头拆掉元组部分) [4, 1, 5]
5 和 4 的确是无法得出谁在谁前,因为 snd (5, False)False,不允许交换的

那只好再审题...

5 没有被手动排序过
我考虑一个列表 [5]
5 排在 1 前面
[5, 1] 证明这个列表是 (>) 降序(Descending)的
可是,开始我直觉上(思路上还是有的)没有考虑到这是一种“部分序列”
4 被手动排序过
4 也在 1 前面
用户选择把 4 放在了一个地方,这个地方符合约束: (> 1)
其实情景是『在自动排序序列中插入一项』?
现在列表看起来有一个不定位置的 4,它必须满足 (4 > 1)

5 和 4 却无法得出谁在谁前
首先:听起来这不像是在写代码,因为 5 和 4 的确可以“比较”吧
除非 4 是用户给出“在 1 前面”的那一个,用户没有告诉我们它是否 (>5) 这样就有两种可能的结果:

[5,4,1] when (4<5)
[4,5,1] when (4>5)

这怎么得出呢?显然这类算法没有简单到一定程度

但以上讨论显然好像是忽略了某些本来存在的信息... 总之,想猜出是“为什么错”的还真是非常困难,因为解决方案和理论错误的都可能是由很奇妙的因素导致的... 可能性有无数种,无数种错误都可能在某个侧面被描述为
5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前
... 看起来是我还不熟悉实际工程?

现在把 5 4 1 泛化成 a b c,升序排列,然后考虑一下实际上如果我们实现某种算法的话会不会出茬子

b, c 是自动排序的项目 且 b < c
[b, c]
现在用户加入了一项 a 且要求 a < c
『要求』怎么抽象?其实可以做一个 lessThanC 集合,如果右项目是 c,则直接判断左项目是否在 lessThanC 布耳映射内否则返回 True(满足序列)
但是就出现了一个问题:
用户只是说 a < c,我们却不知道 a 和 b 什么关系... 看起来好像是必须要实现全函数 ord 的,这问题当然不可解

但依此得出『无法按要求排序列表』,这其实是一个误区... 因为给定的偏序关系本来就已经满足,a 和 b 是什么关系你随便指定,a>b, a<b 都可以。只要 a < c
Given (b < c, a < c)
我们能不能解 a < b?当然不可以,因为显然不能无中生有,如果 a < c、b < a 是能得出 b < c 的(偏序操作符 < 有传递性可推出 b < a < c),但是上面那货只是指定了一个 a 的取值区间,没有具体到拿 b 去限制它,a b 都只是各自 <c 而已

{a b} < c 

...我决定停下,我还有很多有意义的事情可以做,我真的不知道 drakeet 是在写代码的时候(我觉得不太可能,因为这个理论想弄个算法出来想靠模板化代码.... 另外开始就写代码反而不可能想到这个问题,而且以 drakeet 之前的见识这种问题可能要先想,不过我是受教了...)想到的还是在开始想理论的时候弄错的,但不管怎么样、不管为什么错,知道它是错的就好了...

那我等一会就写文吧,不要追想是为啥错的,也怪浪费时间的。(虽然我觉得是想理论模拟比较算法的时候就出了问题,他的思路还的确蛮跳跃的,我都没有把偏序关系抽象成这个样子只是觉得可以『部分排序』和『固定位置』两种,或许是他开始打算使用 Collections API 的 sort 函数吧,那要实现一个 java.util.Comparator<T>


[1] 之所以不称谓词(predicate)是因为我的确很困惑为啥要这么翻译
我开始把它和量词(qunatifier)弄混了,还好很快就明白自己搞错了
这个翻译成谓词(the part of a sentence or clause containing a verb and stating something about the subject)感觉有点不准确(但是谓语『我爱你』的这个的确是谓词),这里我不会用,不过别的地方以后我还是照用不误
#zhihu https://www.zhihu.com/question/28698429
到底是为什么错的啊.... 😭

我真的不太相信 drakeet 真的想到了上面说的那种方法(就是依然用 sort API,但是允许用户利用视图移动指定 a 项目比某一项大或者小,显然这种算法没有那么简单也绝对抄不来,和大学里可能教的冒泡排序是有很大区别的)
即使现在看起来这种情况最有可能导致这种误区...

真的,这就是个无解的问题,到底为什么,只能问他本人,然而他本人就是最开始还没有和我、Telegram 决裂的时候也不愿意多说一个字的相关信息,大概他得把答案带到坟墓里去了...

5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前

我的天啊... 还有什么别的可能没有,几乎我想到每一种可能性都有可能被解释成这个样子,但是只有实现一个 interface java.util.Comparator<T> 的情况最可能

我从『被手动排序』一字猜出了一种限制排序运行(比较时必须两边都『可自动排序』才真正比较)的可能性
从『无法得出』猜出了动态定义偏序关系的可能性

之前我还从排序算法而不是比较算法的角度想过『用户固定位置』的可能性

可是这个题目真正的直觉是啥子,我一根毛都想不到... 这就好像通过一点点蛛丝马迹推断结论之前的成立条件一样... 可能总结出这个结论的算法有一千种,可是最可能的那种到底是什么?
是的,前面 4 句都很正常,可是他是怎么想到最后一句的...

目前就这么说吧:

+ drakeet 不是在实现算法的时候想到这个问题的,而是在想法子实现的时候首先考虑到了这种情况,然后立刻放弃了实现
+ 思路是这样的:
1. 可客观排序列表中有两种项
一种是主观排序项目,按照 Ascending (<) 和 Descending (>) 序来排列
一种是客观排序项目,按照『此项小于某项』来排列
2. 首先验证此模型是否有效

设数据集合 {5, 4, 1} (>) 其中用户指定 4 > 1 也即 1 < 4(用户把 4 移到了 1 之前的位置,定义了这里 4 在 1 前面的关系)
然后 5 本来 (> 1)

首先比较 5, 4 where 4 > 1 可是无法得出 5, 4 谁在谁前(>),因为一个是被用户排序的一个不是,而且因为不知道 4>1 限制有没有导致 5 > 4 ≯ 1 所以这是可能导致结果错误的 (这是个误区,因为排序算法会自动完成偏序关系的传递,不需要比较算法考虑,后者上面的题目没有考虑到,但是这也是正常的,因为本来这么做就屏蔽了部分原生的序列方法)

...

总之.... 这个题目都可能是有问题的,比如是否本应是

5 没有被手动排序过,5 排在 1 后面,4 被手动排序过,所以 4 排在 1 前面,但 5 和 4 却无法得出谁在谁前

这才是真正相悖的结论...
因为 5,4,1 关于 (>) 本身就是一个有序数列... 管他是否有手动排序什么的,看起来更像是『最开始』(还没有考虑要顺序不对可能要交换的情况)的模拟就出错了,因为他觉得无法比较用户限制的对象 (4 where...) 和普通使用原生序的对象 5?这样不那么烧脑一些,也很有可能。

但这等于就是说他完全没有想到啥是偏序关系... 只是 naive 地觉得用户直接安排过后的东西用 < 运算符就不可直接比较了,要不然就会打破用户定义的关系,怎么说也是一种可能,但是否有点太低估人家的意思了。少说人家还写过冒泡排序呢(跑
duangsuse::Echo
到底是为什么错的啊.... 😭 我真的不太相信 drakeet 真的想到了上面说的那种方法(就是依然用 sort API,但是允许用户利用视图移动指定 a 项目比某一项大或者小,显然这种算法没有那么简单也绝对抄不来,和大学里可能教的冒泡排序是有很大区别的) 即使现在看起来这种情况最有可能导致这种误区... 真的,这就是个无解的问题,到底为什么,只能问他本人,然而他本人就是最开始还没有和我、Telegram 决裂的时候也不愿意多说一个字的相关信息,大概他得把答案带到坟墓里去了... 5 没有被手动排序过,5…
Current settled on this. 关于某排序问题,我最终总结『为什么会有这个误解』的结论就是:

(原问题来自 drakeet 的 Android 应用 PureWriter 文章列表实现『同时自动/手动排序』功能)

drakeet 想了这个问题,然后举了一个类似 a > b > c 的(可主观自动排序列表,降序排列, ord=(>))例子
其中 b 是被用户安排的项目,且用户希望 b > c

5 没有被手动排序过,5 排在 1 前面 (5 > 1)
4 被*手动排序*过,4 也在 1 前面 (4 > 1)

他的想法是因为
比较 a (自由项目 "5",线性相关处理后使用 > 操作符直接比较)
b (用户限制项目 "4")不可行(返回什么都可能会打破用户定义的顺序)
(也就是说无法得出 a 和 b 是不是 (a > b) 关系或者 (b > a) 关系,因为 b 是“手动排序的”即便 b 是 5; a 是 4 也不能被比较)

这本身是一个误解,因为用户指定的是 (b > 1) 也就是 (b > c) 仅此而已,和 a 是不是 greaterThan b 无关(对于他的问题答案就是,只要 b>c 就可以,a<b;b<a 你随便选一个喜欢的用户都满意),何况实现『可自动排序同时允许手动排序』的列表也不一定非得采用这种方法,即使只是先分开排序合并再存储下顺序,不需要动态地再次用比较函数排序也可以做到。

我最终从猜到的四种可能里选出这个的原因是,他给出的这个例子本身就符合顺序,看起来就像是才开始想到这个分析方法(此时输入示例都还是有序列表)就断言 ab 因为一个手动一个自动就无法比较;况且如果他开始想使用 Java 的 Collections API 排序静态方法也不是不可能,这些理论基础也是实现时必须首先想到的。

结论当然可能不准确,不过我在这里也得说几句。

+ 首先我可懒得踢什么 drakeet 的馆的(根本不是一个世界的人)也无意诽谤他,对我来说显然比刻意抹黑他而言有更多重要的事,你们现在看到的是技术上对他提出过这个问题的分析,而且之前我还有几次分析或成功或失败。
+ 爱因斯坦说过,如果你不能把道理给六岁小孩讲明白,那你自己其实不理解这个道理。我希望以后如果你要提出的问题和 drakeet 的这个一样

5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前

令人感到莫名其妙,别忘了加点注释、做点总结、多给点信息,别人提出质疑或者帮助的时候也不要一副『我啥都知道了,你肯定是错的』的样子

+ 曾经我觉得任何社交问题,只要不涉及原则性问题,都要尽可能保证双方友好。
也就是说,我不仅默认给所有人基本的尊严和尊重、还包容一切你的自满、骄傲、自以为是、玻璃心、独裁,不愿听取人何关于自己有任何可能负面的话,即使我完全可以说出来,即使我本来没有错。
而且很多人都觉得这么做是理所当然的。

也就是说,不管后来 drakeet 表现有多么奇怪,多么不包容,甚至他有好自满的毛病、因为我提了一个『他错误』的可能他就把我踢了,我都忍了(当然也是有限度的,比如他最后一次毫无理由和预警就踢了我之后我在 60 人的频道上公平客观地评价了一些他的开源项目,虽然现在没人能证明我的评价的确是客观的),我以为就只是表面上开开心心的就好,因为不经常接触,你骄傲一点属于性格不同,我改变不了他的性格,与我无关

人不可有傲气,但不可无傲骨。

可我就没有遇见过和持我不一样态度的人吗?千里冰封 @ice1000 他是真·大佬,真的是很喜欢交朋友而且待人非常宽容的人,表示愿意认识并且确认我的确是沾得上边的同好之后就把我拉进了友群,不过当时我太 naive,即便冰封好友群里的大佬都表示希望我有长进,我还是说了很多很弱智的话(比如一知半解甚至完全不懂的时候我重复过王某 HM 类型系统 SB 的言论,当然那时候我连 HM 的 type variable 都没见过),这时候冰封没有一味地纵容我,把我踢了,但没有直接当傻逼一样屏蔽我(比如我还是可以在 GitHub 上找他,他也依然回复我的问题、Merge 我的 PR)

可是当时我当然是看不出他对我有多么宽容的,甚至包括后来那些话有多愚蠢我也是现在才感受到,班门弄府,而且还是纸做的斧头(工程系的一些人会觉得王垠是天才,他真的是无人能比永远不犯错且任何言论从任何角度看都是正确正确正确的天才呢,还是他扯淡的呢,这话是他的博客标题)还是借来的,真的是无法想像。 若是一个载人航天科学家回复一个农民里的工程师,为什么不用柴油机驱动火箭上天的话,那科学家输了。

后来我把他当时批判我的话发到 Telegram 上,还被人喷他说是不尊重人。
尊重?听起来好像人类社会里,一个啥都不干的懒汉都能得到尊重?那换到计算机科学的群(主要是编译原理\PLT)里,连自己的编译器都没弄出来有什么脸获得『尊重』?当然是先做好自己该做的才能得到自己该有的尊重,尊重不是胡乱发给妄图天上掉馅饼的懒汉的。

也正是因为他们排斥,我才明确地注意到了其实我还真的是太辣鸡了,辣鸡到没有资本和他们一个群讨论也学不到什么,那我就彻底离开冰封,独自想办法学习努力算了。
这个做法才是正确的,差距太大了,想合群就只能潜水,还不如认真在旁边看看感兴趣的东西,也不一定非得加入进去讨论,这也是我一直在做的事情(实际上我也看过很多篇冰封的文章的,我现在 19 年大概只有他 2017 年技术上部分方面的水平... 这就是差距)

曾经我对 drakeet 不仅有对前辈 + 在职(还去过 Alibaba,现在在 M$)工程师的尊重,甚至还包容了他异想天开的逻辑

可是现在我改变主意了,我这么“包容”他,反而是对他不利的,不管我能不能改变别人的性格、我自己采取什么样的策略对待这种性格,世界可未必是这个样子的...

drakeet 就算不打算当“一个高尚的人、一个纯粹的人、一个热爱工作的人、一个摆脱了重复堆砌的人”,但他作为在业的工程师照样要面对无数人,如果我是这个接受一切的特例,是不是反而让他知道了,原来即使保持自己的差性格,原来“冒充先知”其实会被有的人接受?

我还是要和常遇到的人取一个平均... 不能一切都接受了,必要的时候该怼就得怼,实际上这才是真正为他个人着想,也是在为我自己的公平着想。

+ 实践是检验真理的唯一标准

有句话 @drakeet 曾经说过:

[Forwarded from Drakeets]
我觉得 telegram 应该出一个「禁止他人转发私聊消息」功能,总有人私自未经允许将私聊消息转发到各种公开群里,引起不适和曲解。这种人真是非常不礼貌、自私。如果你发现这样的人,还是避免交往比较好。

噢不是这个,这条我支持,不过因为他现在已经气到离开 Telegram 了.... 所以,找不到

大概就是说 Telegram 应该推出禁止转发广播的功能,理由和上面那一条差不多,害怕评论,害怕所谓的『曲解』。

还是之前的事情。

哎说起来 drakeet 最近(2 月)发了一篇 MultiType Recycler ViewHolder dispatch 4.0 库的演示片,要不要去看看(这里就只是技术上,别问我是不是有啥恩怨什么的)

到底是哲学指导实验还是实验指导哲学?(来自《三体 I:地球往事》)

当然是正确的 @drakeet (原文:马克思主义哲学)理论指导实验!

可是这等于说是正确的理论就是从天上掉下来的,反对实践出真知,恰恰是违背马克思主义以实践作为认识自然根本原则的!

为什么说是理论来自实践?为什么认为唯心主义现在不如唯物主义?为什么很多人觉得上帝不存在?

因为科学是基于实践建立起来的,不像神佛鬼怪,科学只是反映了现实,那个所有人都觉得最基本,最不可能改变的客观真理和规律。

真理是不害怕质疑的,质疑只会让完美的真理在谗妄者的诽谤和学者的证明中愈发显出其正确性,真理像是一种生物,它的天敌就是所有想要打倒它、扭曲它、证伪它的人,自然选择是真理诞生和生存中必须要有的经历,因为存在,因为扛住了时间的冲刷打击,所以是真理。

一个人敢于宣称自己说的永远是正确的,最终只会成为暴君,所有人对他任何作品的评论都会被当成是学术欺压,他保留对自己任何作品的解释权、只允许任何人对它们的赞美而任何显得自己没面子的评价都是不可能与之共生的,他运用各种方法屏蔽一切任何对他略有微词的、不利的东西,他保护自己的理论,没有人能说他是错的、他做的是不对的、他弄错了某些东西,可是也惯坏了自己的理论,惯坏了自己的技术,进入这种状态的人终此一生将没有任何实质上的长进,因为他已经屏蔽了进化的源泉。

人们应该追求真理,因为真理让我们变得更好。你觉得真理应该是什么样子,真理应该以什么标准被选择出来?
应该是『某个人说的话全都是真理,我记住它们』还是『一个命题太浅,给我证明它、让我理解它!』?

相信这里的每个人都有自己的答案,当然没有也没办法了,因为对一部分人来说第一个选项在 Telegram 上已经是灰色的了,对另一部分,恐怕也够呛 🌝

中国还是在中国,这种例子很少吗,暴君真的少吗?

#Statement
duangsuse::Echo
现在开始讲解方才 @drakeet 的排序问题 5 没有被手动排序过,5 排在 1 前面,4 被手动排序过,4 也在 1 前面,但 5 和 4 却无法得出谁在谁前 至于这个偏序理论(因为冰封这翻译得很全)(指原文... 因为好像最后没有翻译完)呢,其实是蛮大的知识体系,主要是和数学一些其他的杂七杂八东西比如几何学、集合什么的绑在一起了 https://baike.baidu.com/item/%E5%81%8F%E5%BA%8F%E5%85%B3%E7%B3%BB/943166?fromtitle=…
顺便说一下为啥要进行 n 次,每次都要排序列表从 0 到 n - i -1 的项目,虽然我现在还没有证明为什么 bubble sort 输出的列表就一定是有序的的能力(归纳证明)

排序算法,就是给一个输入序列、一个测试函数『序(order)』,输出序列满足以下条件

forall i. list[i] `order` list[i+1]

比如 list=[3,2,1]; ord=(<) 简单的选择排序每次挑一个『最大』的元素出来(它满足 forall x in list. x <= it),复杂度是 O(n) 当然 n 是输入长度,线性复杂度,这也是必须的(显然得遍历一遍才能找出最大的),没得优化

max [] = undefined
max xs = let (maxidx, max) = (0, xs[0])
in xs.with_index.forEach(cmpChg)
max
  where cmpChg = { |it, idx| if it > max then (maxidx, max) <- (idx, it) else Unit 
}
 

然后每次找到这个最大的,忽略掉它(比如直接 delete),再把它 push 到期待的有序列表里,就成了升序(ascending) 序列(栈是 LIFO 的,collect 到列表后最后 push 进去的最小值成了第一项)

naiveSort xs = let result = []
in xs.length.times do
(maxidx, max) <- max xs
xs.delete(maxidx)
result.push(max)

当然我不知道有没有真正 Haskell 并且还好看的写法,或许依然要枚举索引... 也可以用链表和递归大概,但这里不讲它

对于 bubble sort,首先有一个过程,它遍历一遍所有 list[i] list[i+1],假若 list[i] `ord` list[i+1] 不成立则交换(因为偏序关系有反对称性,所以这是正确的,可以向最终对输出全称量词(forall) 成立的方向演进)
(这里就隐式限定了 i < list.length-1-1 因为 list[?]? 必须 inbounds [0..list.length] 那这里 ? 的最大值是 i+1,最小值是 i
给定 i+1 inbounds 0..list.length 推出(直接用加法的逆运算推导) i inbounds 0..list.length-1 转化为 Java 的布耳表达式就是 (0 <=i && i< list.length-1).

为什么 bubble sort 这种交换排序就需要再循环 n 次呢?而且为什么每一次排序的序列都可以是 [0..tailidx-i] 最后索引递减呢?

对于任何排序算法,都要做到一个最基本的东西:完成对偏序关系的传递
a R b ⇒ b R c ⇒ a R c
对于 R = (<) 的情况就是
a < b ⇒ b < c ⇒ a < c
这样的序列看起来像:
a < b < c
很简单吧
其中快速排序是基于 D&C (一种基于递归的)优雅算法,它对问题解决方式的表达很好地反映了这个道理

(Scala)
def qsort[T](xs: List[T])(implicit ord: Ordering[T]) = {...}
快速排序是一种基于交换的排序算法(这类算法一般都比较高效能,对于原表几乎不需要有任何概念上的数据复制),对问题的解决妙就妙在它把二元关系给“分治”到了二叉树上。
它的递归每层返回一个有序列表、基线是列表已经有序(长度 2 以下)
  if (xs.length <=2) { return xs }

找一个基线,作为交换的基点。
  val pivot: T = xs{xs.length /2}

[1,2,3,4] 基于快速排序它会被分成 [1,2][3,4] 两个子问题(理论上,其实实际使用了优化),
val lts = xs.filter(x => ord.lt(x, pivot))
val gts = xs.filter(x => ord.gt(x, pivot))


最后,返回一个有序的列表,这里用到了递归定义,因为我们不知道子列表是否已经有序,所以要递归下去拿到有序的列表
  return qsort(lts) ++ List(pivot) ++ qsort(gts)

想复制实现代码看这里

每个子问题只有一个子结果:一个有序的列表,实际上这样,它的每一个子问题就只有 O(log2 n) 的时间复杂度,它的传递方式就是『找出每个子树里最大的,然后把它和同层的其他子树做二元比较交换,再次找到最大的比较交换...』这样就在 O(n log2 n)n 是平均情况下要解决子问题的个数,log2 n 是解决每层子问题二元比较的次数,因为它利用递归切分子问题和偏序的传递性,只需要很少的比较次数 log2 n 就可以得出正确的结果)

Question: 如果输入列表长度是单数(odd)岂不是无法切分为两个『子问题』?
Answer: 对于这种情况,设置一个递归分支 qsort [x] = [x] 就好了,这是应对单数输入的情况
此外 qsort [] = [] 也是一种递归中可能发生的情况

==
那现在问题来了,冒泡排序又是怎么完成这个偏序关系传递的呢?
因为冒泡排序比较不适合 Haskell 和 Scala 的风格,就用 Jawa 写好啦!

冒泡排序的思路就是,遍历交换遍历交换... 直到列表有序

有一个基本子,它针对 list[0..ri] 线性(join 每两项,实际上最后真正比较的次数超乎你想象,它做到的『传递』几乎每轮只能增加一个项目... 而不是 i^2 个)排序一遍,这样我们实际上只能保证两边不存在偏序传播问题的项目是完全有序的,因为前面的其他项目的偏序关系全部都没有被传递而只是相邻两项之间有关系而已,(<), [3, 2, 1],最后弄成 [2, 1, 3] 完全有可能,因为它交换 [3,1] 的时候看的是 2 < 3 但是不知道后面还有 2≮1, 和 qsort 基于栈存储的比较次数和每层输出一定有序的断言一比高下立判,优雅性也远远不及后者

private static <T extends Comparable<? super T>> void propagateOnce(List<T> xs, final int ri) {
for (int i =ri -1; i >=0; i--) {
T lhs = xs.get(i), rhs = xs.get(i+1);
boolean ordered = lhs < rhs;
if (!ordered) { // swap!
xs.set(i, rhs); xs.set(i+1, lhs); }
}
}

那既然无法保证 [3,2,1] (<) 之类在 propagateOnce< 的传递性可以被尊重,怎么办?
有一个办法,就是循环 n 次,每次处理 [0..i] 的个子列表,因为虽然像是铁块上的石蜡一样偏序关系无法传递无法透过,边角上的 [2,1] 显然还是能保证顺序的(数学归纳法的基线)
这样处理 (n/2)*n 次过后,实际上就把最刁钻的关系传递到位了(比如 [5, 0, 0, 0, 0, -1] (<) 这个极端情况,会被切分成 5 个子序列从长到短依次串起来副作用排序,-1 会被不断当成边角被“冒泡”到第一个位置,这也是算法名字的来由)

public static <T extends Comparable<? super T>> List<T> bubbleSort(final List<T> xs) {
List<T> output = xs.clone();

for (int rlim =(xs.length-1); rlim >0; rlim--)
{ propagateOnce(output, rlim); }
return output;
}
当然从左边递增切分子序列也是可以的,这里只是因为从右边更符合直觉


刚才看荔枝频道上那个基于遗传算法(物竞天择适者生存)的程序综合(program synthesis)机器人会自己生成排序算法,NB 啊。
是不是 Prolog, Coq 这类描述式编程语言也会这么做呢? #Algorithm #Scala #Java #Haskell
Forwarded from Rachel 碎碎念 (Rachel Mirai | 🏳️‍🌈)
估计消息传开后就
华为:???
媒体:???
鸿蒙:???
最惨的是最后这个