【Meanwhile in LA】0 关于软件度量的入门知识

original_drawn_by_sakeharasu

经过了一段时间的徘徊,从苡瑄那里得知 USC 的指导研究项目今年夏季学期依旧继续,于是尝试报了名;感谢包括 Eliena 在内的所有人给了我一个机会,得以不限于教育学学生的身份参加 CSSE 的研究。我先前并无软件工程的准备知识,只做过一点项目管理的工作,这次可以稍稍深入一点地研究软件度量相关的知识,我觉得很开心。于是想把自己看到想到的写下来,单纯记录也好,与人裨益更佳;若是有人指点就更好了。

软件度量伴随着软件工程的发展而发展,其目的核心与项目管理并无多大区别:其特征在于量化作为工程项目的软件的诸多特征(诸如成本、效能、缺陷),以帮助参与项目的所有人,尤其是项目主导负责人们在软件的生命周期里作出正确的决定。

影响软件度量的事件中,比较有影响力的是 1964 年由 Gene Myron Amdahl 担当架构师(也就是阿姆达尔定律的提出者),由 Frederick P. Brooks, Jr. 担当项目经理的 IBM System/360。为了开发这台电脑,IBM 新招了 6 万名员工,还新开了 5 座工厂;其项目也是雄心勃勃的——他们要藉 OS/360 一统 IBM 的系列产品,告别按电脑型号定制操作系统的旧时代。

IBM System/360 Model 91
One model from IBM System/360 family, retrieved from https://matt.blog/2013/03/08/an-ibm-system-360/
IBM System/360 Models, retrieved from https://en.wikipedia.org/wiki/IBM_System/360

作为蓝色巨人倾国之力打造的里程碑式的作品,OS/360 的血脉流淌在 IBM 此后的每一个大型系统中;而自然,其代价也是昂贵的——这毕竟是人类历史上少有的大型项目,其中遇到的技术之外的困难是众多、难解却弥足珍贵的。Frederick P. Brooks, Jr. 把开发 System/360 和 OS/360 的历程、阻碍和思考在1975年写成了一本书:《The Mythical Man-Month: Essays on Software Engineering》,中文译本标题比较草,叫《人月神话:软件项目管理之道》——“人月”指的是“一个人要花几个月才能开发完一个系统的功能单位”,这是当时衡量软件工时的一个潮流;而神话这个词翻译得就不好了,因为 Mythical 指的是 “迷惑性且不可实现的”。书中的第二部分,便是阐明了人数和必要工时间的非线性关系,指明了人月标准的错误。这和书中的诸多思想一起,成为了对软件工程和软件度量的重要探索基础。

关于软件度量的成文专论,要晚在 1976 年才出版;因为最早的软件度量,是机械地度量行数,看起来没什么必要去写什么专论。有同学可能就要问了,为什么是量行数?你看我 10 行就能解个八皇后问题,遇到不会写 for 循环搞出一万行代码的人我不是亏了嘛;答案很简单——那时的代码可不是我们这种高级语言,都是些指令长度固定、指令集简单、甚至不需要编译器执行、再甚至要打在卡片上的代码。一行代码几乎严格地表示一个操作,也没有什么 attribute 之类的 “标记”,数行数也不是一个大问题;但是后来程序可以用更精简、非面条式的语句执行复杂的结构,单纯地数行数也就没有用了。于是从第一个新概念 “逻辑行数 (Logical Line of Code, LLOC)”对立于“物理行数 (Physical Line of Code)” 开始,人们对软件度量的技术,即“量化能力”提出了新的要求。

根据 Norman F. Schneiderwind 的描述,立体的软件度量包括六个方面:Association、Consistency、Discriminative Power、Tracking、Predictability 和 Repeatability,以 Non-parametric Statistical Method 衡量之。软件度量的五条路径分别是:Function Points、Reliability Reports by Programmers、Halstead Operator Count(For PASCAL)、Metric-based Classification Trees 和 Evaluation of Metrics against Syntactic Complexity Properties。时至今日,很多公司在说的 6-Sigma 以致 CMMI 也是软件度量涉及的东西。

但是在开始软件度量的知识之前,我们要问一个问题,不如说每次打算度量软件或项目之前都必须问一句:我为什么要做软件度量?

比如说我手上有个水壶,我往里倒水,打开炉子,然后想让你告诉我水的温度是多少。

如果你打算用温度计或者测温枪啥的,我建议你再想想——为什么不先问问我烧水做什么呢?如果我想泡个面,那看冒泡了水就开了,没有必要用温度计;如果我拿着上好的抹茶,要严格的87度来泡,那测温枪都不一定好用,你可能会把我的水壶换成恒温慢煮锅才行。

在软件行业里,去量度一个工程,可是远比跑去找个温度计复杂得多,贵得多的。项目一开,公司大门一开,每一秒都带着成本;量度工程需要的心力物力,也自然极为庞大。很多公司的项目主管,接触了软件度量的概念,便为了量化而量化,为了标准而标准,从而造成不必要的开销和损失,这便是“度量陷阱”的一种。作为软件度量的第一步,是千万不要被繁复的数学游戏和标准方法迷了眼睛,要时刻记住自己需要的是什么;更不要奉量化准则为圭臬——有些公司连拿来做量化参照的数据都没攒够,就别搞那些花里胡哨的东西了。

软件度量一直以来,都在关注生命周期内的软件本身。对于贡献者的分析和量化则是后来才有的事。前段时间参加了 ETHDenver 2020 的 BUIDL Week,有见到一个叫 SourceCred 的项目来衡量开源社区参与者对于一个项目的贡献程度,看了看感觉很有意思——因为从哲学上,软件度量都是自上而下的,若是从自下而上试试,也可能会有一条新路。

接下来我打算介绍的是软件度量的既成方法论和变化过程,并看看关于软件行数分析和代码复用有什么可以讲的东西;如果有感兴趣的文章或者点子,请多多指教啦/

【为什么系列】为什么我读不懂数学书?

——那是因为我搞不清形式语言和非形式语言 (v0.02)

自去年开始,自觉之前大学所学的基础学科不够用,想要补补课的我,从 cocoa 留下的那一本离散数学的笔记出发,被 b1 带着钻到计算理论,而后跑到了数学的世界里。作为一个常游泳但嫌水冷都要磨叽半天的我,跳进非舒适区的感觉自然不那么好,于是希望有类似经历的人不再和我一样辛苦得很可怜,就不断地在这边开坑;加之,我想借这个地方纪念我所学的和给我带来帮助的人,我觉得与其期待论文致谢把大家的名字说出来,不如时时用这种方式感谢那些在我平凡日子里嵌进一颗颗宝石的人,所以如果在看这些博客的你有想和我分享的东西,也请不吝赐教哦/

那么 进入正题——

我们大学阶段所看的数学教材,大体是会如此组织:
先是公理,然后是定理,然后可能有一些定义啊、推论、引理(为了证明什么新的东西出来)以及夹杂其间的例子以及证明。然后是习题啊啥啥的;有些实用性强的教材,比如我先前本科经济学的教材,则不注重理论和证明,东西能用就好(何况在高频交易的世界里学了微积分也用不上emmmm)

这样学习的好处是可以不纠结基础理论,也可以说是可以高屋建瓴:不用在数学的大厦里往地基推进。很多人(比如我)能用数学解决问题,但是什么数理逻辑啊模型论啊公理化集合论啊递归论(可计算性理论)还有证明论啥啥的都没听过,也没耽误做医学统计搞经济模型或者机器学习。

但是对于跨学科的人来讲,如果有基础科学的加持,便可以不用在新领域里只得做一个解题能手,因为你可以从更底层去思考问题,这是科班出身的人会有但往往不扎实的能力。

在滚动更新的时候,没必要在学一份“经济数学”再来一份“医学数学”再来一份“计算机数学”的时候,把所有的共通线重刷好几遍——经济数学、医学数学、计算机数学应该像是DLC那样,装载在数学范式的本体上;而对于像我这样先前有安装实用数学(我姑且这么叫)的人,我给自己的建议,还是要把数学的部分滚动更新一下的。

那我来说说为什么我读不懂数学书吧。

简单来讲,最重要的一个事情是:数学语言和我们的日常语言很不一样

这里要讲一个概念叫【元数学】。元数学是指一种“将数学视为人类意识和文化客体的科学思维或知识”,它使用数学技术来解决数学问题,是研究数学和数学哲学的数学。

我可能记不太清了,b1 有一次和我说,主要用非数学语言,比如汉语描述数学的过程,是元数学研究;多讲一句,这算是元数学的三道门中的第一道:
1 将非形式化的理论“形式化”,得到
2 (一般的)形式体系和特殊的形式体系——对象理论,而后
3 借用元理论,描述并研究形式体系 最后使你到到达了数学的世界

在这个过程中,元理论是会使用很多寻常语言的:比如你看哥德尔不完备定理的全文,在他的论述前有 20 多页的前言,全是用非数学语言叙述;论证的过程中,也不像你我看到的数学书那样充满符号。

但是一旦离开了数学的天空之城——元数学的范畴,我们便来到了地上的城——数学。至于说为什么元数学不是数学,可以思考一下理查兹悖论。这个城市和元数学不同,是建立在以“公理化集合论”为代表的“数理逻辑”上的;或者说,当今数学世界的范式,是数理逻辑。这个世界里充满了形式语言,它是用精确的数学或机器可以处理的公式定义的语言;在这里,偶尔出现的非形式化语言只能给人家做个点缀(这时候我们称这种形式化非形式化掺杂的语言为‘半形式化语言’)

形式语言和非形式语言虽然都有语言学上的“语法”和“语义”,但它们行同陌路。
从最小的符号的定义上都截然不同。这便是为什么我看数学系的教材往往是似懂非懂的。

你看,我们定义非形式语言为“一套复合交流系统”;定义形式语言为“一个字母表上的某个有限长字符串的集合”。真的你瞅瞅,这俩东西咋看咋不是一家的。

所以说了这么多,搞不明白形式语言和非形式语言的区别,拿着应用数学的知识和非形式化语言的逻辑,想看懂数学的教材,对于我们这些外行人来讲一定是相当困难的事情。想要搞懂数学书上是什么内容,我认为要从熟悉形式语言和符号入手;若是懂一些公理化集合论和数理逻辑,那就最好了。

那么最后问一个问题,为什么形式语言都写在数学书里呢?

要回答这个问题,我想让你先想象自己是像欧拉那样的,每天脑子里都会蹦出改变世界的新想法的天才数学家。别谦虚,想就是了w

你的脑子里有新的点子:于你而言,你是有一套天才的证明方法的;事实上,我相信每个数学家都有自己特色的核心处理法,直觉也算。但是你的证明方法别人不一定能听懂,你的灵感源泉无法向别人解释,更要命的是你无法百分百证明自己的点子是正确的,因为你的核心处理法不一定百试百灵。那怎么办?

为了能够跟你眼中够不上你的大多数人,或是和你有不同研究个性的同侪们解释你的点子,你首先要尝试把它翻译成人人都能精准理解的某种语言,然后再给对方。不论是解释、证明还是争辩,你都要用这种精准的、没有歧义的语言来转换你的点子、你的想法;这样你便保证了你思想的果实可以完整地传递给他人。当然他人怎么把这种中间语言转换成他的理解那就不是我们要管的事儿了。——精准地传递信息,这就是形式语言的意义。这种经过公认考验和提炼的精准语言的格式,我们称为数学范式。

有了形式语言的另一个好处是,我们可以划定处理问题的边界,或者不确切地说,“我们所表述所研究的数学理论”是什么。确切来讲则是:相对于元数学,数学是什么。我们还是假设你是一个为你所在的某个领域做出贡献的数学家,你希望你所做的描述不只符合某种特殊情况,而是尽可能地具有普适性,可以用在各种具有某些共同性质的东西上。我们便把这些共同性质抽提成为一个集合的性质/运算,把那些具有共同性质的东西抽提为集合中的元素,这种带有元素还带有运算的东西,我们可以称之为“空间”;这种公论承认、无需证明的共同性质,我们叫它们“公理”。比如构造线性空间的 8 条公理:

线性空间的公理化定义,截图自 https://zh.wikipedia.org/wiki/向量空间, 2020-03-08

只要你想让你的理论和概念在这个空间里适用,那么你一定要满足这 8 条公理;如果别的数学家或者你的学生搞出了新的想法,你也可以用“这些想法是否满足这 8 条公理”来验证。把公理作为集合的元素后,我们便有了“公理系统”;把公理和它们导出的所有定理总括起来,我们把它称为“数学理论”。数学理论的范围,可以说便是相对于元数学的数学世界了。我没有使用“范畴”这个词,是因为不想让你重写范畴论里的范畴定义w

形式/半形式语言写就的公理,和你在书上看到的那些形式化/半形式化语言一样,它们不是自然法则,它们是一座座桥,连接着你、你的同侪和那些伟大的数学家。为了这座桥的平整牢靠,我们使用“抽象化”来处理这座桥的模型,便成就了它极简抽象、晦涩难懂的风格。你的目的和困难,在于把桥的模型了然于胸;至于之后你如何装点你心里的桥,那便是你的事情了。

最后,形式语言、非形式语言、公理化集合论、数理逻辑等等的定义本身,维基百科说得肯定比我好,那样的知识也俯拾即是,我便不再赘言了w

下一次,我想讲一讲“为什么我看不懂数学符号”
或 “为什么我看不懂数学证明”

The Way to an ‘Impossible Program’ 1: Automaton, Algorithm, and Church-Turing Thesis

Outputs from Computation Theory Lectures

From the Automata Theory before we moved to Computability Theory and Computational Complexity Theory, we had gone through several Formal Languages, Formal Grammars and Abstract Machines. For the lecture of COS487/MAT407 in Princeton, Formal System was the next topic for students’ discussion; but as long as I’m writing these posts in a line for everyone has the same amateur knowledge basis like me, I’d like to put the First-Order-Logic a bit off, and continue to explaining the Computability Theory in advance.

Speaking of those Abstract Machines, we can see the limitations each one of them easily: Due to the absence of storage, a Finite State Machine (FSM) can’t deal with those problems with ‘counting infinitely’ (eg., determine if a string ‘balanced’ with a and b, or \(L = \{a^n b^n | n ≥ 0 \}\)); a Pushdown Automaton (PDA) has infinite storage, but that storage is still a stack. That means a PDA must consume the input characters ‘in the order’ in which they are received, and cannot access them again, except by placing them on the stack. Therefore, a PDA can’t determine if a string satisfies such rules like ‘a string has the same amount of particular symbols in total’ (eg. “aacbcbdd”); besides, it only works on Context-Free…

Until we got A Turing machine – it has unlimited storage can be visited so efficiently under any order, any loop, any condition and any sub-function. Life is great ;D

Until, we reached the boundary of it. We added more features to those Turing machines but didn’t expand any additional abilities to them, by proving that those features can be replaced by another (bunch of) designed Turing Machines, if those features are applicable. Then where is that limitation of a Turing Machine? Is there an impossible program?

Those ‘General Systems’ such as Turing Machines are dealing with algorithms, not to mention computers. Then, to answer those questions, we have to see the features of an Algorithm. If we know the algorithms’ features and abilities, we might figure out what those limitations are.

In a total rudimentary way to explain, an algorithm is a set of instructions, each instruction takes an/ε input(s) and got an output of it/them. But there are 4 rules applied:

  • The number of instructions are finite;
  • The instructions can’t be complex;
  • The whole process with instructions can be done in finite length of time, if those instructions are followed/used correctly;
  • The result out of those sets of instructions must be ‘correct’ if those instructions are followed/used correctly.

Based on the 4 rules above, we can turn numerous algorithms into programming codes. But is it possible to convert each and every algorithm into a set of instructions for general systems’ execution? People (especially people writing codes) may just answer ‘yes’, following their instincts. Yet under the realm of Computational Architecture, the direct, abstractive idea of an algorithm is totally unequal to its logical, concrete implementation.

So again, can we design an algorithm with such unbelievable complexity and unmeasurable scale that no machine can grasp its stem?

Since 1930s, computer scientists have been working on creating an algorithm which could not be ‘solved’ by an abstract machine – every attempt of creating an model of computation returns a system, and those systems were all proved to have the equivalent capacity of a Turing machine, whereas there’s still no mathematical / logical proof for that general question. In fact, from the place I put up the 4 rules for algorithm, we are not be able to talk about the whole question on the level of logic or mathematics any more. But at least, along the way of exploration, we are confident to accept the hypothesis below:

“Every algorithm has the features as I mentioned in this post, can be executed by an abstract machine, or specifically, a Turing machine.” or let’s say, “a function on the natural numbers can be calculated by an effective method if and only if it is computable by a Turing machine.”

And here we reached the first stop of the Computability Theory: Church-Turing Thesis,

“We shall use the expression ‘computable function’ to mean a function calculable by a machine, and let ‘effectively calculable’ refer to the intuitive idea without particular identification with any one of these definitions.”

Turing, Alan (1938). Systems of Logic based on Ordinals (PhD thesis). Princeton University. doi: 10.1112/plms/s2-45.1.161.

or in Gandy’s(1980:123) words, every effectively calculable function is a computable function.

Despite of the form of Church-Turing Thesis is a ‘thesis’, a literature statement than a logical, formal expression, the Church-Turing Thesis came out from solid formalisms: the λ-calculus, besides recursion, and the Turing machine. Those are the topics up next, which requires we write something to show the power of those 3.

We’ll begin from the λ-calculus.

【公式编辑测试+小题目w】

1 试计算概率论公式:

$\varphi(x)=\frac{1}{2\pi\sigma_1\sigma_2}\int^{+\infty}_{-\infty}e^{-\frac{\xi^2}{2\sigma_1^2}}e^{-\frac{(x-\xi)^2}{2\sigma_2^2}}d\xi\quad(\delta_1>0,\delta_2>0).$

2 契贝谢夫-厄尔米特多项式由公式
\begin{equation}
H_n(x)=(-1)^ne^{x^2}\frac{d^n}{dx^n}(e^{-x^2}) \quad(n=0,1,2,\cdots)
\end{equation}
而定义,试证明
\begin{equation}
\int^{+\infty}_{-\infty}H_m(x)H_n(x)e^{-x^2}dx=
\left\{
\begin{aligned}
&0,& 若m\neq n\\
&2^nn!\sqrt{\pi},&若m=n
\end{aligned}
\right.
\end{equation}

3 用latex编公式好好玩www

$\forall_{x_i}\exists_{a}a \wedge b \vee \neg c$什么的www

啊对了,

题目1 2来源于《数学分析习题集(吉米多维奇)》题目3839和3840。

3839的答案为$(\varphi)x=\frac{1}{\sigma\sqrt{2\pi}}e^{-(\frac{x^2}{2\sigma^2})},$

其中,$\sigma=\sqrt{\sigma_1^2+\sigma_2^2}$.

N个女人的老死不相往来——n皇后问题

知乎问题链接:如何用C++10行内写出八皇后

前日闲暇之余乱逛,看到了这个问题,觉得蛮有意思。
也顺便想改进一下通行的官方解法,就认真写了一下,
顺便也把自己作为外行人的一点感想列在下面。

n皇后问题,较早为八皇后问题,出现于1848年。
Max Bezzel讨论的是“在国际象棋上出现8个后时,
应该如何排列,才可使她们不会相爱相杀。”

(八皇后问题的其中一个解)

虽然依照升变原则,同棋盘上可以出现17个后,
(一方全升变,一方被吃过路兵一次,加上初始两个)
但是真正走出来的可能性是微乎其微的。
况且这个问题没有考虑黑白方关系,
在象棋上也就是趣味的一问而已。
——直到图论和函数式编程思想出现,
穷举法和递归思想使得92个解终于呼之即出。

前面说到,n皇后问题的解法都是穷举法及递归,
加上回溯算法实现。这对于刚入门的傻喵来讲,
是很好的练习题目了——

' n queens cast by go basic
[loop]
input "How many queens (N>=4)";n
if n < 4 then
 print "Must be greater than 4"
 goto [loop]
end if
 
dim plot$(100,100)
dim q(n+20)
dim e(n+20)
dim o(n+20)
r=n mod 6
if r<>2 and r<>3 then 
  gosub [samp]
  goto [shoBoard]
end if
for i=1 to int(n/2)
  e(i) = 2 * i
next
for i=1 to int((n/2)+.5)
 o(i) = 2 *i-1
next
if r = 2 then gosub [edt2]
if r = 3 then gosub [edt3]
s = 1
for i=1 to n
  if e(i)>0 then 
    q(s) = e(i)
    s    = s+1
  end if
next
for i=1 to n
  if o(i) > 0 then 
    q(s) = o(i)
    s    = s + 1
  end if
next
' print board
[shoBoard]
cls
for i = 1 to n
  plot$(i,26-q(i)) = "*"
  plot$(i,24-n)    = chr$(96+i)
  plot$(n+1,26-i)  = str$(i)
next i
for ii = 1 to 100
 for jj = 1 to 100
  print left$(plot$(jj,ii)+" ",1);
 next jj
print
next ii
end
 
' the simple case
[samp] 
p = 1
for i = 1 to n
  if i mod 2=0 then 
    q(p) = i
    p    = p + 1
  end if
next i
for i = 1 to n
  if i mod 2 then 
    q(p) = i
    p    = p + 1
  end if
next
return
' edit list when remainder is 2
[edt2]
for i=1 to n
  if o(i) = 3 then 
    o(i) = 1 
   else 
    if o(i)=1 then o(i) = 3
  end if
  if o(i) = 5 then 
    o(i)= o(i) -1 
   else 
    if o(i) = 0 then 
      o(i) = 5
      return
    end if
  end if
next
 
' edit list when remainder is 3
[edt3]
for i = 1 to n
  if e(i) = 2 then 
    e(i)  = e(i)-1 
   else 
    if e(i) = 0 then 
      e(i) = 2
      goto [more]
    end if
  end if
next i
' edit list some more
[more]
for i = 1 to n
  if (o(i)=1 or o(i)=3) then 
    o(i) = o(i)-1 
   else 
    if o(i) = 0 then 
      o(i)   = 1
      o(i+1) = 3
      return
    end if
  end if
next

或是这样的尝试——

# Brute force permutations for 8 queens by R

safe <- function(p) {
  n <- length(p)
  for (i in seq(1, n - 1)) {
    for (j in seq(i + 1, n)) {
      if (abs(p[j] - p[i]) == abs(j - i)) return(F)
    }
  }
  return(T)
}
 
queens <- function(n) {
  p <- 1:n
  k <- 0
  while (!is.null(p)) {
    if(safe(p)) {
      cat(p, "\n")
      k <- k + 1
    }
    p <- next.perm(p)
  }
  return(k)
}
 
queens(8)

在好多人的口中,递归+穷举已然成为了8皇后问题的标准答案。
但是时代还是会变的,人的能力也是在随着问题进步的。
当面临N皇后问题时,传统的方法则捉襟见肘。

虽然有人推荐直接向上抽象,利用已有的库来简化方法,
但是私以为,练习归练习,学习归学习,但不能只会做一个传道者,
迷信工具,迷信顶层,而没有透彻的基础认识。
工具看了手册谁都会用,为什么用,怎么用,好不好则更加重要。
就像是学习语言或是诊断学,迷信书本只知所以然是万万不可的。
——只会利用工具的人,终究会被工具抛弃。

回到问题上来。

N皇后问题中,穷举法的复杂度为(n!),
但若是采用和某些逻辑题一样的算法,则可以大大简化代码。

首先,国际象棋的后拥有两种攻击方式:
好似城堡的十字攻击以及骑士的对角攻击,攻击范围无限制:

从而得出两个预结论——
1 N皇后于NxN棋盘上,若老死不相往来,则绝不在同一行列。
2 攻击范围有重叠时,绝非边与边重叠,只可相交,且相交仅会出现一次。
于是便可以将棋盘格子赋予是否安全的属性,1为受到攻击位置,0安全。
从第一条边向第八条边推进,标注所有可能的攻击位置,发现0则置子,
发现一个以上的安全位置,则从一个方向起下子,深度优先遍历。

于是使用位运算,将n~1横列化为n个二进制数,指定0位安全;
由预结论1,不考虑所下子横行上的攻击(下子后必然全为1);
由预结论1,copy下子所在竖列的状态至下一行;
利用位移运算符定义两种对角攻击模式。
以上图第4横行为例,下子状态标注为 0001 0000
(计算第5横行时,下子状态保留为 0001 0000,
攻击范围则将下子状态左移/右移一位,
分别是 0010 0000 和 0000 1000。
将三个状态列取或运算,则结果中的0位为安全位置。

于是——

#include <iostream>

// total:记录解;finishline:确认的行
int total = 0, finishline = 1;

// row:竖直攻击; ls:斜杠攻击; rs:反斜杠攻击
void trying(long row, long ls, long rs)
{
	if (row != finishline) // 开始新行
	{
		long position = finishline & ~(row | ls | rs);
		while(position) // 若行间无位可放...
		{
			long pointline = position & -position;
			position -= pointline;
			trying(row + pointline, (ls + pointline) << 1, (rs + pointline) >> 1);
		}
	}
	else 
	{
		total++;
	}
}

using namespace std;

int main(int argc, char *argv[]) {
	int grid = 8; // 修改grid的话,1~31皇后都能解w

finishline = (finishline << grid) - 1;

trying(0, 0, 0);

cout << total << endl;

return 0;

}

事就这么成了。

回到最早的知乎问题上去。我真的不明白为什么要限制行数,
而不是限制时间和内存,
但也因为这个问题看似哗众取宠,
便在想是不是某种形式的软广告。

不过最有意思的事情是问题下面百花齐放的代码。
有人简化变量名到一个字符,有人省略头文件,
有人把所有的东西挤成一行,自然注释都没有。
不用说美感,就连维护都是问题。
自己都看不懂了,就为了10行,图啥?
可谓是为完成任务而完成任务了的哗众取宠了。
还有人直接就import <8queens>,
抑或是贬损除了暴力枚举之外的其他算法,
真的是不知道什么逻辑了。

学习计算机科学到现在,傻喵越来越觉得,
好多东西都是如此相通的——
会用工具却拘泥于工具的人;
认为掌握工具便算精通的人;
知道规则便想创造规则的人;
末法心态的,实用主义的人。
希望我不会变成他们那样。。。

更愿意从数学汇编一步步学起,
就像当年从解剖学一步步学起,
就像当年从桥本文法开始学起,
就像当年从吹长音一步步学起,
这样,我想算是专业的负责吧。

不管。总之先把题答了——

#include <iostream>
int total = 0, finishline = 1;
void trying(long row, long ls, long rs){
if (row != finishline) { long position = finishline & ~(row | ls | rs);
while(position) { long pointline = position & -position; position -= pointline; trying(row + pointline, (ls + pointline) << 1, (rs + pointline) >> 1); }} else {total++;}} 
using namespace std;
int main(int argc, char *argv[]) {
int grid = 8; // 修改grid的话,1~31皇后都能解w
finishline = (finishline << grid) - 1; trying(0, 0, 0); cout << total << endl;
return 0;}

嗯。能看得懂,10行也就差不多这样了。

不过,如果八皇后问题真的会在项目里用到,
我则绝不会使用上面的所有程序,而会——

// 此处需使用八皇后算法,但忽略计算过程,仅使用答案节省时间。
// 八皇后解枚举数据头在******
void 8queensAnswers (0, 0, 0, 0)
//在里面做一个调用92个答案的接口,传参出去给大结构体
//保存各个答案类似于[91, 0, 4, 7, 5, 2, 6, 1, 3]这样
//或者n皇后的话[0,1][1,0][2,0][3,2][4,10][5,40]...这样

复杂度为1或是常数n,为啥不用?多好。

至于说怎么显示N皇后问题的解的话w
手动艾特@CocoaOikawa~

N皇后问题——记录下那些老死不相往来的N个女人的位置吧

以上ヾ(๑╹◡╹)ノ”