精华内容
下载资源
问答
  • 复杂性
    千次阅读
    2019-11-08 19:44:19

    密码必须符合复杂性要求

    介绍 "密码必须满足复杂性要求" 安全策略设置的最佳做法、位置、值和安全注意事项。

    参考

    "密码必须满足复杂性要求" 策略设置确定密码是否必须满足一系列对强密码重要的指南。 启用此策略设置需要密码才能满足以下要求:

    在更改或创建密码时, 将强制执行复杂性要求。

    Windows Server 密码复杂性要求中包含的规则属于 Passfilt, 不能直接修改。

    启用默认的 Passfilt 可能会导致对锁定帐户的其他帮助台呼叫, 因为用户可能不会使用包含字母表中所示字符以外的其他字符的密码。 但是, 此策略设置非常灵活, 所有用户都可以通过次要学习曲线遵守要求。

    可包含在自定义 Passfilt 中的其他设置是使用非上层行字符。 较高的字符是通过按住 SHIFT 键并键入任何数字 from1 through10 所键入的字符。

    可能值

    最佳实践

    设置密码必须满足复杂性要求才能启用。 此策略设置与 "最小密码长度 of8" 相结合, 可确保单个密码至少有218340105584896种不同的可能性。 这使得强力攻击非常困难, 但仍不可能。

    使用 ALT 键字符组合可以大大提高密码的复杂程度。 但是, 要求组织中的所有用户遵守严格的密码要求可能会导致不满意的用户和极其繁忙的技术支持。 请考虑在你的组织中实现一个要求, 以使用0128到0159范围内的 ALT 字符作为所有管理员密码的一部分。 (此范围外的可选字符可以表示不会为密码增加额外复杂性的标准字母数字字符。)

    仅包含字母数字字符的密码可通过公共可用工具轻松受到危害。 为防止这种情况, 密码应包含其他字符并满足复杂性要求。

    位置

    计算机 Configuration\Windows Settings\Security Settings\Account Policies\Password 政策

    默认值

    下表列出了实际和有效的默认策略值。 默认值也在策略的属性页上列出。

    服务器类型或组策略对象 (GPO)默认值
    默认域策略启用
    默认域控制器策略启用
    独立服务器默认设置禁用
    域控制器有效默认设置启用
    成员服务器有效的默认设置启用
    客户端计算机上的有效 GPO 默认设置禁用

    安全注意事项

    本部分介绍攻击者如何利用一项功能或其配置,如何实施对策,以及对策实施可能产生的负面后果。

    漏洞

    仅包含字母数字字符的密码非常容易发现使用多个公开可用的工具。

    对策

    将 "密码必须满足复杂性要求" 策略设置配置为 "已启用", 并建议用户使用其密码中的各种字符。

    结合使用最小密码长度8 时, 此策略设置可确保单个密码的不同可能性的数量非常大, 以至于非常困难 (但不可能), 强力攻击才能成功。 (如果 "最小密码长度" 策略设置增加, 则成功的攻击所需的平均时间也会增加。)

    潜在影响

    如果保留默认密码复杂性配置, 则可能会发生锁定帐户的其他支持呼叫, 因为用户可能不熟悉包含非字母字符的密码, 或者可能在输入时遇到问题包含具有不同布局的键盘上的重音字符或符号的密码。 但是, 所有用户都应能够以最少的难度满足复杂性要求。

    如果您的组织具有更严格的安全要求, 则可以创建 Passfilt 文件的自定义版本, 该版本允许使用任意复杂的密码强度规则。 例如, 自定义密码筛选器可能需要使用非上层行符号。 (较高的行符号是要求您长按 SHIFT 键, 然后按1到0之间的任何数字的符号。)自定义密码筛选器还可能执行字典检查, 以验证建议的密码是否不包含常见字典单词或片段。

    使用 ALT 键字符组合可以大大提高密码的复杂程度。 但是, 这种严格的密码要求可能会导致其他帮助台请求。 或者, 你的组织可以考虑对所有管理员密码的要求, 以便在0128–0159范围中使用 ALT 字符。 (此范围外的可选字符可以表示不会为密码增加额外复杂性的标准字母数字字符。)

    1. 密码不能包含用户的 samAccountName (帐户名称) 值或整个 displayName (全名)。 这两个检查不区分大小写。

      将完全检查 samAccountName, 以确定它是否是密码的一部分。 如果 samAccountName 的长度小于3个字符, 将跳过此检查。 将分析 displayName 的分隔符: 逗号、句点、短划线或连字符、下划线、空格、井号和制表符。 如果找到这些分隔符中的任何一个, 则将拆分 displayName, 并且将确认所有已分析的分区 (标记) 不包含在密码中。 将忽略小于3个字符的标记, 并且不检查标记的子字符串。 例如, 名称 "Erin Hagens" 分为三个标记: "Erin"、"M" 和 "Hagens"。 由于第二个令牌的长度仅为一个字符, 因此将被忽略。 因此, 此用户在密码中的任何地方都不能包含 "erin" 或 "hagens" 作为子字符串的密码。

    2. 密码包含以下类别中的三个:

      • 欧洲语言的大写字母 (A 到 Z, 带有音调符号标记、希腊语和西里尔文字符)
      • 欧洲语言的小写字母 (a 到 z、半高和音调符号标记、希腊语和西里尔文字符)
      • 基数10个数字 (0 到 9)
      • 非字母数字字符 (特殊字符): (~! @ # $% ^& * _-+ = "| \ (){}\ []:;" "<>,。?/) 此政策设置不会将货币符号 (如欧元或英国镑) 计为特殊字符。
      • 归类为字母字符但不大写或小写的任何 Unicode 字符。 这包括亚洲语言的 Unicode 字符。
    • Enabled
    • 禁用
    • 未定义
    更多相关内容
  • 领域驱动设计:软件核心复杂性应对之道.pdf
  • 降低软件复杂性的一般原则和方法

    千次阅读 2019-09-24 10:37:31
    分模块降低了单模块的复杂性,但是也会引入新的复杂性,例如模块与模块的交互,后面的章节会讨论这个问题。这里,我们将第三个原则确定为分模块。 最后,代码能够描述程序的工作流程和结果,却很难描述开发人员的...

    一、前言

    斯坦福教授、Tcl语言发明者John Ousterhout 的著作《A Philosophy of Software Design》[1],自出版以来,好评如潮。按照IT图书出版的惯例,如果冠名为“实践”,书中内容关注的是某项技术的细节和技巧;冠名为“艺术”,内容可能是记录一件优秀作品的设计过程和经验;而冠名为“哲学",则是一些通用的原则和方法论,这些原则方法论串起来,能够形成一个体系。正如”知行合一”、“世界是由原子构成的”、“我思故我在”,这些耳熟能详的句子能够一定程度上代表背后的人物和思想。用一句话概括《A Philosophy of Software Design》,软件设计的核心在于降低复杂性。

    本篇文章是围绕着“降低复杂性”这个主题展开的,很多重要的结论来源于John Ousterhout,笔者觉得很有共鸣,就做了一些相关话题的延伸、补充了一些实例。虽说是"一般原则“,也不意味着是绝对的真理,整理出来,只是为了引发大家对软件设计的思考。

    二、如何定义复杂性

    关于复杂性,尚无统一的定义,从不同的角度可以给出不同的答案。可以用数量来度量,比如芯片集成的电子器件越多越复杂(不一定对);按层次性[2]度量,复杂度在于层次的递归性和不可分解性。在信息论中,使用熵来度量信息的不确定性。

    John Ousterhout选择从认知的负担和开发工作量的角度来定义软件的复杂性,并且给出了一个复杂度量公式:

    子模块的复杂度cp乘以该模块对应的开发时间权重值tp,累加后得到系统的整体复杂度C。系统整体的复杂度并不简单等于所有子模块复杂度的累加,还要考虑该模块的开发维护所花费的时间在整体中的占比(对应权重值tp)。也就是说,即使某个模块非常复杂,如果很少使用或修改,也不会对系统的整体复杂度造成大的影响。

    子模块的复杂度cp是一个经验值,它关注几个现象:

    • 修改扩散,修改时有连锁反应。
    • 认知负担,开发人员需要多长时间来理解功能模块。
    • 不可知(Unknown Unknowns),开发人员在接到任务时,不知道从哪里入手。

    造成复杂的原因一般是代码依赖和晦涩(Obscurity)。其中,依赖是指某部分代码不能被独立地修改和理解,必定会牵涉到其他代码。代码晦涩,是指从代码中难以找到重要信息。

    三、解决复杂性的一般原则

    首先,互联网行业的软件系统,很难一开始就做出完美的设计,通过一个个功能模块衍生迭代,系统才会逐步成型;对于现存的系统,也很难通过一个大动作,一劳永逸地解决所有问题。系统设计是需要持续投入的工作,通过细节的积累,最终得到一个完善的系统。因此,好的设计是日拱一卒的结果,在日常工作中要重视设计和细节的改进。

    其次,专业化分工和代码复用促成了软件生产率的提升。比如硬件工程师、软件工程师(底层、应用、不同编程语言)可以在无需了解对方技术背景的情况下进行合作开发;同一领域服务可以支撑不同的上层应用逻辑等等。其背后的思想,无非是通过将系统分成若干个水平层、明确每一层的角色和分工,来降低单个层次的复杂性。同时,每个层次只要给相邻层提供一致的接口,可以用不同的方法实现,这就为软件重用提供了支持。分层是解决复杂性问题的重要原则。

    第三,与分层类似,分模块是从垂直方向来分解系统。分模块最常见的应用场景,是如今广泛流行的微服务。分模块降低了单模块的复杂性,但是也会引入新的复杂性,例如模块与模块的交互,后面的章节会讨论这个问题。这里,我们将第三个原则确定为分模块。

    最后,代码能够描述程序的工作流程和结果,却很难描述开发人员的思路,而注释和文档可以。此外,通过注释和文档,开发人员在不阅读实现代码的情况下,就可以理解程序的功能,注释间接促成了代码抽象。好的注释能够帮助解决软件复杂性问题,尤其是认知负担和不可知问题(Unknown Unknowns)。

    四、解决复杂性之日拱一卒

    4.1 拒绝战术编程

    战术编程致力于完成任务,新增加特性或者修改Bug时,能解决问题就好。这种工作方式,会逐渐增加系统的复杂性。如果系统复杂到难以维护时,再去重构会花费大量的时间,很可能会影响新功能的迭代。

    战略编程,是指重视设计并愿意投入时间,短时间内可能会降低工作效率,但是长期看,会增加系统的可维护性和迭代效率。

    设计系统时,很难在开始阶段就面面俱到。好的设计应该体现在一个个小的模块上,修改bug时,也应该抱着设计新系统的心态,完工后让人感觉不到“修补”的痕迹。经过累积,最终形成一个完善的系统。从长期看,对于中大型的系统,将日常开发时间的10-15%用于设计是值得的。有一种观点认为,创业公司需要追求业务迭代速度和节省成本,可以容忍糟糕的设计,这是用错误的方法去追求正确的目标。降低开发成本最有效的方式是雇佣优秀的工程师,而不是在设计上做妥协。

    4.2 设计两次

    为一个类、模块或者系统的设计提供两套或更多方案,有利于我们找到最佳设计。以我们日常的技术方案设计为例,技术方案本质上需要回答两个问题,其一,为什么该方案可行? 其二,在已有资源限制下,为什么该方案是最优的?为了回答第一个问题,我们需要在技术方案里补充架构图、接口设计和时间人力估算。而要回答第二个问题,需要我们在关键点或争议处提供二到三种方案,并给出建议方案,这样才有说服力。通常情况下,我们会花费很多的时间准备第一个问题,而忽略第二个问题。其实,回答好第二个问题很重要,大型软件的设计已经复杂到没人能够一次就想到最佳方案,一个仅仅“可行”的方案,可能会给系统增加额外的复杂性。对聪明人来说,接受这点更困难,因为他们习惯于“一次搞定问题”。但是聪明人迟早也会碰到自己的瓶颈,在低水平问题上徘徊,不如花费更多时间思考,去解决真正有挑战性的问题。

    五、解决复杂性之分层

    5.1 层次和抽象

    软件系统由不同的层次组成,层次之间通过接口来交互。在严格分层的系统里,内部的层只对相邻的层次可见,这样就可以将一个复杂问题分解成增量步骤序列。由于每一层最多影响两层,也给维护带来了很大的便利。分层系统最有名的实例是TCP/IP网络模型。

    在分层系统里,每一层应该具有不同的抽象。TCP/IP模型中,应用层的抽象是用户接口和交互;传输层的抽象是端口和应用之间的数据传输;网络层的抽象是基于IP的寻址和数据传输;链路层的抽象是适配和虚拟硬件设备。如果不同的层具有相同的抽象,可能存在层次边界不清晰的问题。

    5.2 复杂性下沉

    不应该让用户直面系统的复杂性,即便有额外的工作量,开发人员也应当尽量让用户使用更简单。如果一定要在某个层次处理复杂性,这个层次越低越好。举个例子,Thrift接口调用时,数据传输失败需要引入自动重试机制,重试的策略显然在Thrift内部封装更合适,开放给用户(下游开发人员)会增加额外的使用负担。与之类似的是系统里随处可见的配置参数(通常写在XML文件里),在编程中应当尽量避免这种情况,用户(下游开发人员)一般很难决定哪个参数是最优的,如果一定要开放参数配置,最好给定一个默认值。

    复杂性下沉,并不是说把所有功能下移到一个层次,过犹不及。如果复杂性跟下层的功能相关,或者下移后,能大大下降其他层次或整体的复杂性,则下移。

    5.3 异常处理

    异常和错误处理是造成软件复杂的罪魁祸首之一。有些开发人员错误的认为处理和上报的错误越多越好,这会导致过度防御性的编程。如果开发人员捕获了异常并不知道如何处理,直接往上层扔,这就违背了封装原则。

    降低复杂度的一个原则就是尽可能减少需要处理异常的可能性。而最佳实践就是确保错误终结,例如删除一个并不存在的文件,与其上报文件不存在的异常,不如什么都不做。确保文件不存在就好了,上层逻辑不但不会被影响,还会因为不需要处理额外的异常而变得简单。

    六、解决复杂性之分模块

    分模块是解决复杂性的重要方法。理想情况下,模块之间应该是相互隔离的,开发人员面对具体的任务,只需要接触和了解整个系统的一小部分,而无需了解或改动其他模块。

    6.1 深模块和浅模块

    深模块(Deep Module)指的是拥有强大功能和简单接口的模块。深模块是抽象的最佳实践,通过排除模块内部不重要的信息,让用户更容易理解和使用。

    Unix操作系统文件I/O是典型的深模块,以Open函数为例,接口接受文件名为参数,返回文件描述符。但是这个接口的背后,是几百行的实现代码,用来处理文件存储、权限控制、并发控制、存储介质等等,这些对用户是不可见的。

    int open(const char* path, int flags, mode_t permissions);
    
    

    与深模块相对的是浅模块(Shallow Module),功能简单,接口复杂。通常情况下,浅模块无助于解决复杂性。因为他们提供的收益(功能)被学习和使用成本抵消了。以Java I/O为例,从I/O中读取对象时,需要同时创建三个对象FileInputStream、BufferedInputStream、ObjectInputStream,其中前两个创建后不会被直接使用,这就给开发人员造成了额外的负担。默认情况下,开发人员无需感知到BufferedInputStream,缓冲功能有助于改善文件I/O性能,是个很有用的特性,可以合并到文件I/O对象里。假如我们想放弃缓冲功能,文件I/O也可以设计成提供对应的定制选项。

    
    FileInputStream fileStream = new FileInputStream(fileName);
    BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
    ObjectInputStream objectStream = new ObjectInputStream(bufferedStream);
    
    

    关于浅模块有一些争议,大多数情况是因为浅模块是不得不接受的既定事实,而不见得是因为合理性。当然也有例外,比如领域驱动设计里的防腐层,系统在与外部系统对接时,会单独建立一个服务或模块去适配,用来保证原有系统技术栈的统一和稳定性。

    6.2 通用和专用

    设计新模块时,应该设计成通用模块还是专用模块?一种观点认为通用模块满足多种场景,在未来遇到预期外的需求时,可以节省时间。另外一种观点则认为,未来的需求很难预测,没必要引入用不到的特性,专用模块可以快速满足当前的需求,等有后续需求时再重构成通用的模块也不迟。

    以上两种思路都有道理,实际操作的时候可以采用两种方式各自的优点,即在功能实现上满足当前的需求,便于快速实现;接口设计通用化,为未来留下余量。举个例子。

    
    void backspace(Cursor cursor);
    void delete(Cursor cursor);
    void deleteSelection(Selection selection);
    
    //以上三个函数可以合并为一个更通用的函数
    void delete(Position start, Position end);
    
    

    设计通用性接口需要权衡,既要满足当前的需求,同时在通用性方面不要过度设计。一些可供参考的标准:

    • 满足当前需求最简单的接口是什么?在不减少功能的前提下,减少方法的数量,意味着接口的通用性提升了。
    • 接口使用的场景有多少?如果接口只有一个特定的场景,可以将多个这样的接口合并成通用接口。
    • 满足当前需求情况下,接口的易用性?如果接口很难使用,意味着我们可能过度设计了,需要拆分。

    6.3 信息隐藏

    信息隐藏是指,程序的设计思路以及内部逻辑应当包含在模块内部,对其他模块不可见。如果一个模块隐藏了很多信息,说明这个模块在提供很多功能的同时又简化了接口,符合前面提到的深模块理念。软件设计领域有个技巧,定义一个"大"类有助于实现信息隐藏。这里的“大”类指的是,如果要实现某功能,将该功能相关的信息都封装进一个类里面。

    信息隐藏在降低复杂性方面主要有两个作用:一是简化模块接口,将模块功能以更简单、更抽象的方式表现出来,降低开发人员的认知负担;二是减少模块间的依赖,使得系统迭代更轻量。举个例子,如何从B+树中存取信息是一些数据库索引的核心功能,但是数据库开发人员将这些信息隐藏了起来,同时提供简单的对外交互接口,也就是SQL脚本,使得产品和运营同学也能很快地上手。并且,因为有足够的抽象,数据库可以在保持外部兼容的情况下,将索引切换到散列或其他数据结构。

    与信息隐藏相对的是信息暴露,表现为:设计决策体现在多个模块,造成不同模块间的依赖。举个例子,两个类能处理同类型的文件。这种情况下,可以合并这两个类,或者提炼出一个新类(参考《重构》[3]一书)。工程师应当尽量减少外部模块需要的信息量。

    6.4 拆分和合并

    两个功能,应该放在一起还是分开?“不管黑猫白猫”,能降低复杂性就好。这里有一些可以借鉴的设计思路:

    • 共享信息的模块应当合并,比如两个模块都依赖某个配置项。
    • 可以简化接口时合并,这样可以避免客户同时调用多个模块来完成某个功能。
    • 可以消除重复时合并,比如抽离重复的代码到一个单独的方法中。
    • 通用代码和专用代码分离,如果模块的部分功能可以通用,建议和专用部分分离。举个例子,在实际的系统设计中,我们会将专用模块放在上层,通用模块放在下层以供复用。

    七、解决复杂性之注释

    注释可以记录开发人员的设计思路和程序功能,降低开发人员的认知负担和解决不可知(Unkown Unkowns)问题,让代码更容易维护。通常情况下,在程序的整个生命周期里,编码只占了少部分,大量时间花在了后续的维护上。有经验的工程师懂得这个道理,通常也会产出更高质量的注释和文档。

    注释也可以作为系统设计的工具,如果只需要简单的注释就可以描述模块的设计思路和功能,说明这个模块的设计是良好的。另一方面,如果模块很难注释,说明模块没有好的抽象。

    7.1 注释的误区

    关于注释,很多开发者存在一些认识上的误区,也是造成大家不愿意写注释的原因。比如“好代码是自注释的"、"没有时间“、“现有的注释都没有用,为什么还要浪费时间”等等。这些观点是站不住脚的。“好代码是自注释的”只在某些场景下是合理的,比如为变量和方法选择合适的名称,可以不用单独注释。但是更多的情况,代码很难体现开发人员的设计思路。此外,如果用户只能通过读代码来理解模块的使用,说明代码里没有抽象。好的注释可以极大地提升系统的可维护性,获取长期的效率,不存在“没有时间”一说。注释也是一种可以习得的技能,一旦习得,就可以在后续的工作中应用,这就解决了“注释没有用”的问题。

    7.2 使用注释提升系统可维护性

    注释应当能提供代码之外额外的信息,重视What和Why,而不是代码是如何实现的(How),最好不要简单地使用代码中出现过的单词。

    根据抽象程度,注释可以分为低层注释和高层注释,低层次的注释用来增加精确度,补充完善程序的信息,比如变量的单位、控制条件的边界、值是否允许为空、是否需要释放资源等。高层次注释抛弃细节,只从整体上帮助读者理解代码的功能和结构。这种类型的注释更好维护,如果代码修改不影响整体的功能,注释就无需更新。在实际工作中,需要兼顾细节和抽象。低层注释拆散与对应的实现代码放在一起,高层注释一般用于描述接口。

    注释先行,注释应该作为设计过程的一部分,写注释最好的时机是在开发的开始环节,这不仅会产生更好的文档,也会帮助产生好的设计,同时减少写文档带来的痛苦。开发人员推迟写注释的理由通常是:代码还在修改中,提前写注释到时候还得再改一遍。这样的话就会衍生两个问题:

    • 首先,推迟注释通常意味着根本就没有注释。一旦决定推迟,很容易引发连锁反应,等到代码稳定后,也不会有注释这回事。这时候再想添加注释,就得专门抽出时间,客观条件可能不会允许这么做。
    • 其次,就算我们足够自律抽出专门时间去写注释,注释的质量也不会很好。我们潜意识中觉得代码已经写完了,急于开展下一个项目,只是象征性地添加一些注释,无法准确复现当时的设计思路。

    避免重复的注释。如果有重复注释,开发人员很难找到所有的注释去更新。解决方法是,可以找到醒目的地方存放注释文档,然后在代码处注明去查阅对应文档的地址。如果程序已经在外部文档中注释过了,不要在程序内部再注释了,添加注释的引用就可以了。

    注释属于代码,而不是提交记录。一种错误的做法是将功能注释放在提交记录里,而不是放在对应代码文件里。因为开发人员通常不会去代码提交记录里去查看程序的功能描述,很不方便。

    7.3 使用注释改善系统设计

    良好的设计基础是提供好的抽象,在开始编码前编写注释,可以帮助我们提炼模块的核心要素:模块或对象中最重要的功能和属性。这个过程促进我们去思考,而不是简单地堆砌代码。另一方面,注释也能够帮助我们检查自己的模块设计是否合理,正如前文中提到,深模块提供简单的接口和强大的功能,如果接口注释冗长复杂,通常意味着接口也很复杂;注释简单,意味着接口也很简单。在设计的早期注意和解决这些问题,会为我们带来长期的收益。

    八、后记

    John Ousterhout累计写过25万行代码,是3个操作系统的重要贡献者,这些原则可以视为作者编程经验的总结。有经验的工程师看到这些观点会有共鸣,一些著作如《代码大全》、《领域驱动设计》也会有类似的观点。本文中提到的原则和方法具有一定实操和指导价值,对于很难有定论的问题,也可以在实践中去探索。

    关于原则和方法论,既不必刻意拔高,也不要嗤之以鼻。指导实践的不是更多的实践,而是实践后的总结和思考。应用原则和方法论实质是借鉴已有的经验,可以减少我们自行摸索的时间。探索新的方法可以帮助我们适应新的场景,但是新方法本身需要经过时间检验。

    九、参考文档

    • John Ousterhout. A Philosophy of Software Design. Yaknyam Press, 2018.

    • 梅拉尼·米歇尔. 复杂. 湖南科学技术出版社, 2016.

    • Martin Fowler. Refactoring: Improving the Design of Existing Code (2nd Edition) . Addison-Wesley Signature Series, 2018.

    作者介绍

    政华,顺谱,陶鑫,美团打车调度系统工程团队工程师。

    招聘信息

    美团打车调度系统工程团队诚招高级工程师/技术专家,我们的目标,是与算法、数据团队密切协作,建设高性能、高可用、可配置的打车调度引擎, 为用户提供更好的出行体验。欢迎有兴趣的同学加入呦~~

    展开全文
  • 算法复杂性渐近阶的分析

    千次阅读 2019-07-26 00:31:03
    算法复杂性渐近阶的分析 前两段讲的是算法复杂性渐近阶的概念和对它进行分析的重要性。本段要讲如何具体地分析一个算法的复杂性的渐近阶,给出一套可操作的规则。算法最终要落实到用某种程序设计语言(如Pascal)编...

    算法复杂性渐近阶的分析

    前两段讲的是算法复杂性渐近阶的概念和对它进行分析的重要性。本段要讲如何具体地分析一个算法的复杂性的渐近阶,给出一套可操作的规则。算法最终要落实到用某种程序设计语言(如Pascal)编写成的程序。因此算法复杂性渐近阶的分析可代之以对表达该算法的程序的复杂性渐近阶的分析。

    如前所提出,对于算法的复杂性,我们只考虑最坏、最好和平均三种情况,而通常又着重于最坏情况。为了明确起见,本段限于针对最坏情况。

    仍然以时间复杂性为例。这里给出分析时间复杂性渐近阶的八条规则。这八条规则已覆盖了用Pascal语言程序所能表达的各种算法在最坏情况下的时间复杂性渐近阶的分析。

    在逐条地列出并解释这入条规则之前,应该指出,当我们分析程序的某一局部(如一个语句,一个分程序,一个程序段,一个过程或函数)时,可以用具体程序的输入的规模N作为复杂性函数的自变量,也可以用局部的规模参数作为自变量。但是,作为最终结果的整体程序的复杂性函数只能以整体程序的输入规模为自变量。

    对于串行的算法,相应的Pascal程序是一个串行的Pascal语句序列,因此,很明显,该算法的时间复杂性(即所需要的时间)等于相应的Pascal程序的每一个语句的时间复杂性(即所需要的时间)之和。所以,如果执行Pascal语句中的每一种语句所需要的时间都有计量的规则,那么,执行一个程序,即执行一个算法所需要的时间的计量便只是一个代数问题。接着,应用本节第三段所提供的Ο、Ω和θ等运算规则就可以分析出算法时间复杂性的渐近阶。

    因此,我们的时间计量规则只需要针对Pascal有限的几种基本运算和几种基本语句。下面是这些规则的罗列和必要的说明。

    规则(1)

    赋值、比较、算术运算、逻辑运算、读写单个常量或单个变量等,只需要1个单位时间。

    规则(2)

    条件语句"if C then S1 else S2"只需要Tc+max(Ts1,Ts2)的时间,其中Tc是计算条件表达式C需要的时间,而Ts1和Ts2分别是执行语句S1和S2需要的时间。

    规则(3)

    选择语句"Case  A  of  a1:S1; a2:S2; … ;am:Sm;  end",需要max(Ts1, Ts2,…,Tsm)的时间,其中Tsii是执行语句Si所需要的时间,i=l,2,…,m。

    规则(4)

    访问一个数组的单个分量或一个记录的单个域,只需要1个单位时间。

    规则(5)

    执行一个for循环语句需要的时间等于执行该循环体所需要的时间乘上循环的次数。

    规则(6)

    执行一个while循环语句"while C do S"或一个repeat循环语句" repeat S until C",需要的时间等于计算条件表达式C需要的时间与执行循环S体需要的时间之和乘以循环的次数。与规则5不同,这里的循环次数是隐含的。

    例如,b_search函数中的while循环语句。按规则(1)-(4),计算条件表达式" (not found)and(U≥=L)"与执行循环体

    I:=(U+L)div 2;
    if c=A[I] then found:=true
    else  if  c>A[I] then L:=I+1
    else U:=I-1;

    只需要θ(1)时间,而循环次数为logm,所以,执行此while语句只需要θ(logm)时间。

    在许多情况下,运用规则(5)和(6)常常须要借助具体算法的内涵来确定循环的次数,才不致使时间的估计过于保守。这里举一个例子。

    考察程序段:

     
     

    Size:=m;

    1

    i:=1;

    1

    while i<n do

     

        begin

     

          i:=i+1;

     

          S1;

    θ( n)

          if  Size>0  then

    1

             begin

     

             在1到Size的范围内任选一个数赋值给t;

    θ(1)

                 Size:=Size-t;

    2

                 for j:=l  to  t  do

     

                     S2

    θ( n)

             end;

     

        end;

     
       

    程序在各行右端顶格处标注着执行相应各行所需要的时间。如果不对算法的内涵作较深入的考察,只看到1≤t≤Size≤m,就草率地估计while的内循环for的循环次数为Ο(m),那么,程序在最坏情况下的时间复杂性将被估计为Ο(n 2+m·n 2)。反之,如果对算法的内涵认真地分析,结果将两样。事实上,在while的循环体内t是动态的,size也是动态的,它们都取决while的循环参数i,即t=t(i)记为ti;size=size(i)记为sizei ,i=l,2,…,n-1。对于各个i,1≤i≤n-1,tim的关系是隐含的,这给准确地计算for循环的循环体S2被执行的次数带来困难。上面的估计比较保守的原因在于我们把S2的执行次数的统计过于局部化。如果不局限于for循环,而是在整个程序段上统计S2被执行的总次数,那么,这个总次数等于img13.gif,又根据算法中ti的取法及sizei+1=sizei-ti,i=1,2,…,n-1 有sizen=size1-img4.gif。最后利用size1=m和sizen=0得到img14.gif=m 。于是在整个程序段上,S2被执行的总次数为m,所需要的时间为θ(mn)。执行其他语句所需要的时间直接运用规则(l)-(6)容易计算。累加起来,整个程序段在最坏情况下时间复杂性渐近阶为θ(n 2+mn)。这个结果显然比前面粗糙的估计准确。

    规则(7)

    对于goto语句。在Pascal中为了便于表达从循环体的中途跳转到循环体的结束或跳转到循环语句的后面语句,引入goto语句。如果我们的程序按照这一初衷使用goto语句,那么,在时间复杂性分析时可以假设它不需要任何额外的时间。因为这样做既不会低估也不会高估程序在最坏情况下的运行时间的阶。如果有的程序滥用了goto语句,即控制转移到前面的语句,那么情况将变得复杂起来。当这种转移造成某种循环时,只要与别的循环不交叉,保持循环的内外嵌套,则可以比照规则(1)-(6)进行分析。当由于使用goto语句而使程序结构混乱时,建议改写程序然后再做分析。

    规则(8)

    对于过程调用和函数调用语句,它们需要的时间包括两部分,一部分用于实现控制转移,另一部分用于执行过程(或函数)本身,这时可以根据过程(或函数)调用的层次,由里向外运用规则(l)-(7)进行分析,一层一层地剥,直到计算出最外层的运行时间便是所求。如果过程(或函数)出现直接或间接的递归调用,则上述由里向外逐层剥的分析行不通。这时我们可以对其中的各个递归过程(或函数),所需要的时间假设为一个相应规模的待定函数。然后一一根据过程(或函数)的内涵建立起这些待定函数之间的递归关系得到递归方程。最后用求递归方程解的渐进阶的方法确定最坏情况下的复杂性的渐进阶。

    递归方程的种类很多,求它们的解的渐近阶的方法也很多,我们将在下一段比较系统地给予介绍。本段只举一个简单递归过程(或函数)的例子来说明如何建立相应的递归方程,同时不加推导地给出它们在最坏情况下的时间复杂性的渐近阶。

    例:再次考察函数b_search,这里将它改写成一个递归函数。为了简明,我们已经运用前面的规则(l)-(6),统计出执行各行语句所需要的时间,并标注在相应行的右端:

     

     

    Function b_search(C,L,U:integer):integer;

    单位时间数

    var index,element:integer;

     

     begin

     

       if (U<L) then

         1

                b_search:=0;

         1

       else

     

        begin

     

          index:=(L+U) div 2;

         3

          element:=A[index];

         2

          if element=C then

         1

            b_search:=index

         1

       else if element>C then

     

            b_search:=b_search(C,L,index-1)

       3+T(m/2)

          else

     

            b_search:=b_search(C,index+1,U);

       3+T(m/2)

        end;

     

     end;

     
      

    其中T(m)是当问题的规模U-L+1=m时b_search在最坏情况下(这时,数组A[L..U]中没有给定的C)的时间复杂性。根据规则(l)-(8),我们有:

    img16.gif

    或化简为

    img19.gif

    这是一个关于T(m)的递归方程。用下一段将介绍的迭代法,容易解得:

    T(m)=11logm +l3=θ(logm)

    在结束这一段之前,我们要提一下关于算法在最坏情况下的空间复杂性分析。我们照样可以给出与分析时间复杂性类似的规则。这里不赘述。然而应该指出,在出现过程(或函数)递归调用时要考虑到其中隐含的存储空间的额外开销。因为现有的实现过程(或函数)递归调用的编程技术需要一个隐含的、额外(即不出现在程序的说明中)的栈来支持。过程(或函数)的递归调用每深人一层就把本层的现场局部信息及调用的返回地址存放在栈顶备用,直到调用的最里层。因此递归调用一个过程(或函数)所需要的额外存储空间的大小即栈的规模与递归调用的深度成正比,其比例因子等于每深入一层需要保存的数据量。比如本段前面所举的递归函数b_search,在最坏情况下,递归调用的深度为logm,因而在最坏情况下调用它所需要的额外存储空间为θ(logm)。

    转载于:https://www.cnblogs.com/tongzhiyong/archive/2007/04/01/695969.html

    展开全文
  • 复杂性研究面临的难题

    千次阅读 2018-01-02 00:00:00
    一、什么是复杂性目前无法表述清楚 在汉语中“复杂”一词的意思为“事物的种类、头绪等多而杂”。在《朗文当代英语词典》中,形容词complex被解释为:(1)难于理解、解释或处理,不清楚或不简单; (2)由许多密切...

    一、什么是复杂性目前无法表述清楚

        在汉语中“复杂”一词的意思为“事物的种类、头绪等多而杂”。在《朗文当代英语词典》中,形容词complex被解释为:(1)难于理解、解释或处理,不清楚或不简单;  (2)由许多密切相关的部分所组成的;(3)(词或句子)由主要部分和其余部分组成的。日常用语的“复杂”词义实际涉及两个方面:其一,客观的本体论方面它指事物的组成杂而多}其二,主体的认识论方面它指难于理解和解释,不容易处理,不清楚。  据美国记者约翰。霍根(John Horgan)在其著作《科学的终结》中所讲,麻省理工学院的物理学家塞思·劳埃德(Seth Lloyd)通过电子邮件向他提供了一份关于复杂性定义的清单,他统计了一下有45种:(1)信息,(2)熵,(3)算法复杂性,(4)算法信息量,(5)费希尔信息,(6) Renyi熵,(7)自描述代码长度,(8)矫错代码长度,(9) Chernoff信息,(10)最小描述长度,(11)参量个数或自由度或维数,(12) I_empel-Ziv复杂性,(13)共有信息或通道容量,(14)演算共有信息,(15)相关性,(16)储存信息,(17)条件信息,(18)条件演算信息量,(19)计量嫡,(zo)分形维,(21)自相似,(22)随机复杂性,(23)混和,  (24)拓扑机器容量,  (25)有效或理想的复杂性,(26)分层复杂性,(27)树形多样性,(28)同源复杂性,(29)时问计算复杂性,(30)空间计算复杂性,(31)基于信息的复杂性,(32)逻辑深度,(33)热力学深度,(34)规则复杂性,(35)信息Kullbach-Liebler(36)区别性,  (37)费希尔距离,  (38)分辨力,  (39)信息距离,(40)滇算信息距离,(41) Hamming距离,(42)长幅序,(43)自组织,(44)复杂适应系统,(45)混沌边缘。

        复杂性的定义远不止45种。据笔者2007年在网络google上的一次查询,输入复杂性一词,有3410000项与复杂性相关的同页。其中涉及算法复杂性、计算复杂性、生物复杂性、生态复杂性、演化复杂性、发育复杂性、语法复杂性,乃至经济复杂性、社会复杂性等。实际上几乎每个学科的研究者对复杂性都有各自的见解,要想在目前阶段搞清复杂性的含义太难了,几乎是不可能的事,复杂性概念的模糊性对复杂性科学概念的形成和复杂性科学体系建立有一定负面影响。复杂性概念的模糊性、不确定性反映了复杂性研究的不成熟。

        当然,复杂性概念的模糊性和不确定性并不绝对妨碍人们去研究具体对象的复杂性问题,也不影响人们对复杂性研究的各种理论和方法的构建。人们可以结合各种具体对象来讨论复杂性问题,可以在各自的理论中给复杂性以可操作的定义。我国著名科学家钱学森认为,所谓“复杂性”实际是开放的复杂巨系统的动力学,或开放的复杂巨系统学。钱学森在简单性与复杂性问题之间划了一道界线,他认为:“凡是不能用还原论方法处理或不宜用还膘论方法处理的问题,都是复杂性问题,复杂巨系统就是这类问题。”[  在哲学领域,复杂性分为客观复杂性和认识论复杂性:(1)从本体论角度界定的复杂性。颜泽贤等对复杂性进行了如下定义:“复杂性是客观事物的一种属性。复杂性是客观事物层次之间的一种跨越。复杂性是客观事物跨越层次的不能够用传统的科学学科理论直接还原的相互关系。”吴彤提出了客观复杂性的概念,客观复杂性包括三个方面;结构复杂性、边界复杂性、运动复杂性。其中结构复杂性又可分为:分形结构复杂性和非稳定结构复杂性;运动复杂性也可分为:分岔运动意义的复杂性、突变运动复杂性、混沌运动意义的复杂性。(2)认识论角度定义的复杂性。描述复杂性,其含义为:在得到关于某物的组成部分和这些部分之间的相互关系几乎全部信息的情况下,用某种语言对该物的整体行为进行精确的形式表述的困难程度。认识论的复杂性依狡于主体的认识,主体选择的语言形式和表述的类型,还依赖于认识者的兴趣、能力和观念等。

    二、复杂系统的基本特征厦复杂性产生机制说法不一

        有的学者概括了复杂系统的十大特征:

       (1)多样性。复杂系统是由大量不同层次、不同类型的组分及组分之间广泛而大量的联系构成的对象,系统中的每一个单元的变化都会受到其他单元的影响,同时也影响其他单元的变化。

       (2)多层次性。系统具有多层次、多功能的结构,多组成即有多种多样的子系统和子子系统,每个子系统有独立的结构、功能与行为;每一层次均成为构筑其上一层次的单元,同时也有助于系统的某一功能的实现。

       (3)整体性。复杂性体现整体性系统性。对于一个复杂的非线性系统,系统的整体行为并非简单地与子系统的行为相联系,不能简单地从局部的个别行为的细节去判断整体。系统的整体行为不是胼有局部行为的简单相加,即每个组成不能代替整体,每个层次的局部不能说明整体。

       (4)开放性。复杂系统是开放系统,它与环境有密切关系,能与环境相互作用,与外部环境是统一的,并能不断向更好地适应环境的方向发展变化。

       (5)非平衡性。复杂性只能产生于远离平衡态。系统通过自组织形成耗散结构,复杂系统大多是非平衡态的系统。

       (6)非线性。复杂系统通常都是由许多子系统组成,各组成之间相互关联,相互制约,以某种或多种方式发生复杂的非线性相互作用,非线性是系统复杂性的内在机制。

       (7)动态演化性。复杂系统是动态的,处于不断的演化当中,具有自适应和进化能力。系统的动态演化过程是阶段性的,有渐变与突变,整个过程是非线性的。渐变是突变的基础,而突变则是从低级到高级的变化原因。可以把突变作为一个系统由一种阶段、一种状态向另一种阶段、另一种状态演化的转折。

       (8)不确定性。不确定性首先是源于随机性的复杂性。它对初始条件、参数和环境的微小扰动具有高度的敏感性,即所谓“差之毫厘,失之千里”,系统的状态极其不稳定。另一种重要的不确定性为模糊性,模糊系统显示的行为既不是完全有序的和可以预测的,也不是完全随机的和绝对不可预测的,经常处于模糊的边界,或介于二者之间。因为有序态、随机态和混沌态等多样性行为可以共同存在于一个复杂系统中,它们能够依系统内外不同参数条件随时间和空间而变化,显示出多姿多态。模糊性既是复杂性的来源,又是复杂性的表现或结果。

       (9)自组织性。自组织性是开放系统在大量子系统合作下出现的宏观上的新结构。系统随着时间而变化,经过系统内部和系统与环境的相互作用,不断适应、调节,通过自组织作用,经过不同阶段和不同过程,向更高级的有序化发展,涌现出独特的整体行为与特征。系统有自适应、自组织地趋向有序化的功能。

       (10)不可逆性。可逆过程没有复杂性,还原论方法足以解决问题。复杂性只能出现于不可逆过程。在复杂系统中,时间反演的不可逆性和不对称性,产生r对称破缺,从而造成系统演化的复杂性。

    有的研究者将复杂系统的特征简化为五大特征:

    1. 组分数目巨大,复杂系统拥有数目巨大的组分,系统因规模增大而复杂。

    2. 组分间存在着复杂的相互作用,这种复杂性表现在,相互作用不一定只是物理性质,还可能是信息的交换;相互作用必须达到一定程度;相互作用是非线性的;组分同作用较为直接、短程性(shortrange);相互作用有反馈的环路。(3)开放性。(4)远离平衡态。(5)历史(生成性),复杂系统都是有历史的,它们不仅是在时间中演化,而且现在的行为依赖于过去。(6)信息不完全性。任何组分个体都无法预知自己的行为会对整体产生怎样的影响,复杂性是组分个体间丰富的相互作用的结果,但这些组分个体(适应性主体)却只能对与自身有关的信息作出反应。 

    还有的研究者用四不来描述复杂性:(1)不连续性;(z)不确定性;(3)不可分离性;(4)不可预测性。认为,未来不是完全可以预测的,未来不在过去的延长线上。未来并非过去的继续,而是一系列的不连续事件。只有承认这种不连续性并设法适应它,我们才有机会在21世纪生存下来并获得成功。有的研究者从定性与定量的角度来界定复杂性。“从定量上讲,复杂系统有高阶次、高维度、多回路、多输人、多输出和层次性等特点。从定性上讲,复杂系统具有非线性、不确定性、内外部扰动、多时空、开放性、自相似性、病态结构及混沌现象等特征”,“其中,涌现性和非线性是复杂系统最本质的特点”。

    3.复杂性研究的一般性理论比较薄弱 

    (1)缺乏合适的核心范畴、逻辑出发点和研究路径。目前的研究主要是以系统和挝程概念为核心范畴,是围绕系统展开的研究,这种系统范式以组成系统的要素作为研究的逻辑出发点,通过对要素之间的关系即结构的研究达到对系统整体性质与功能的研究,进而描述系统的状态与过程。这种范式的缺陷或困惑是,从局部即要素出发的研究由于系统整体的非线性,从局部推论不出整体,即不能从局部来认识整体,称为系统的不可分性。而选择从结构出发的研究也走不通,根据关系的形成规律,要素的数量越多关系形成的可能性就越多,如果要素超过一定数量又服从全排列,则关系数量呈指数增长变成超计算问题,即使有算法,用计算方法也研究不了此类复杂性问题。如果选择系统作为研究问题的出发点也有很大的麻烦,现有的系统概念主要是从局整关系角度考虑问题的,系统与时间维度的关系不确定,这样一个时间边界不确定的对象作为复杂性研究的出发点也是不合适的。复杂性研究的逻辑出发点是立足于动静关系即状态或过程,还是立足于局整关系即要素和系统,是从相对静止的状态出发来建构理论体系,还是从要素、结构到整体涌现的系统出发来建构理论体系,其结果是大不一样的。所以,目前的系统范式正在经历从系统到复杂性,从复杂性到困惑的道路。

       (2)现有的复杂性研究大多数是具体科学层次或工程层次的研究,而属于一艘性、共性的研究则较少。近几年来的复杂性研究主要集中在复杂系统的演化、涌现、自组织、自适应、自相似的机理研究。在金融避险与经济动力学、脑高级功能的复杂性、灾害系统、演化计算、元胞自动机、多智能体(Multi-Agents)复杂网络等方面,大都是具体科学或技术层次的研究。目前,复杂性研究的一般性理论比较薄弱,从学科发展的整体看,复杂系统理论就是要研究解决复杂系统中的共性问题即复杂性问题,具体科学层次的研究、工程层次的研究不能代替复杂性共性的一般性研究,褴有共twj-般性理论的指导,具体对象的复杂性研究不可能走得太远。

         4. 具体科学领域对人的思维系统的复杂性研究难班深入

        复杂系统研究领域中研究得比较多的是“地球物理系统、深太空系统、生物与生态系统、人工生命系统、自适应进化系统、人工智能系统、社会自组织系统、经济管理系统、军事作战系统及复杂工程系统”。.胡而对于世界上最复杂最有挑战意义的人的思维系统的研究相对薄弱,有的复杂性研究几乎不提思维系统的复杂性研究问题,急功if利和知难而退的回避策略被不少研究者采用。然而,思维系统的复杂性研究不可回避,其研究也不可能只让心理学研究者来单独完成。

        5. 琨在的方法和技术不能满足复杂性研究和应用的需要

       (1) 用简单科学中的概念、方法和技术来解决复杂性问题,将复杂性当简单性来研究。在技术方法上过度依赖符号方法及技术。简单性对象及过程用符号方法及技术可以解决,但复杂性对象用符号方法及技术失效。一部分研究者习惯于用计算等硬方法来解决复杂性问题,实际上复杂性对象的表达多数是超计算、不可计算的,符号方法最终将收效甚微。早期的复杂性研究主要用定量化、形式化方法描述和解决复杂性问题。迄今为止,中国和西方的科学家已经创造了一系列的复杂系统研究方法和技术,如模型方法,数值方法,计算方法,模拟和虚拟方法,综合集成方法,物理、事理、人理方法,动力学方法,隐喻方法等软科学与硬科学结合的方法。但仅用这些方法来处理复杂性对象还是远远不够的。

        (2) 在研究中混淆研究对象的不同层次。部分理论将不同的历时态层次混淆,如将系统、状态和过程三个历时态层次混淆,将系统、状态和过程中不同的复杂性混淆,将非线性与非确定性混淆,将连续性与非连续性混淆,将可以用符号方法解决的(线性)问题与不可以用符号方法解决的(非线性)问题混在一起,使一些本来可以解决的问题与不可解决的问题混在一起。其主要原因是使用了没有时间边界或时间边界不确定的系统概念为研究的逻辑出发点。

       (3) 对复杂性对象特征的认识片面,观控技术欠缺。有的理论认为复杂性的根本特征只是非绒性,状态论认为复杂性对象的主要特征可以概括为四非:即非线性、非确定性、非连续性、非预测性,相应地在认识论和技术层次表现为四个失效:线性方法失效,确定性方法失效,连续方法失效,预测方法失效。四个失效几乎把人类历史上已经成功把握的,已经取得伟大辉煌成就的传统科学方法放到了无用武之地的位置上去了。那么,目前人们除了定性的、隐喻的、直觉的、灵感的方法外,还有什么方法来对付复杂性对象呢?复杂性是否是人的认识的局限?是否是人类不能跨越的门槛?复杂性研究的危机和困惑由此产生。笔者以为,复杂性研究在认识论和技术层次的四个失效实际上反映了人类长期以来使用的符号方法及技术的失效,要从整体上解决复杂性问题,人类还需要新的思路和方法,特别需要发展处理非符号信息的技术和方法。



    展开全文
  • 算法复杂性

    千次阅读 2017-10-22 10:56:14
    一、算法 算法是指解决问题的一种方法或一个过程,更...算法复杂性分为时间复杂性和空间复杂性(目标:设计出复杂度尽可能低的算法)。  2.1 空间复杂性  算法运行时需要空间资源的量  2.2时间复杂性  
  • 复杂性研究从20世纪末叶兴起,目前在国内外已成为许多学科领域内研究的前沿和热点。它涉及又一个新型的跨学科的方法论。虽然人们对“复杂性”概念还缺乏严格一致的定义,但大家都意...
  • 图(1)报错截图 具体解决办法: 第一步 打开服务器管理 如图直接在 放大镜搜索框里面 ,直接输入搜索服务器管理或者打开箭头3所示的图标 ...最后点击密码必须符合复杂性要求改为已启用 ...
  • 复杂性思维第二版 一、复杂性科学

    万次阅读 2017-10-27 21:44:26
    一、复杂性科学 原文:Chapter 1 Complexity Science 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 这本书的论点是,复杂性科学是一种“新型科学”,我借鉴自 Stephen Wolfram。2002年,Wolfram ...
  • 文章目录 一、coNP 类 二、coNP 完全 三、P、NP、coNP 相互关系 一、... 计算理论只关注 P\rm PP , NP\rm NPNP , NP\rm NPNP 完全 三个复杂类 , 这是三个最基本的复杂类 , 通过三个基本复杂类可以衍生无数个复杂类 ;
  • 转自百度网盘:https://pan.baidu.com/s/1dD1pDDJ
  • PMPtiku项目管理第六版项目的复杂性

    千次阅读 2018-05-29 18:09:29
    文_PMPtiku.com1,项目的复杂性来源于组织的系统行为,人类行为以及组织或环境中的不确定性。...3,复杂性的三个维度定义为:(1)系统行为(组成部分与系统之间的依赖关系);(2)人类行为(不同个体和群...
  • 领域驱动设计:软件核心复杂性应对之道 修订版 pdf

    千次阅读 多人点赞 2020-09-25 10:15:33
    链接:https://pan.baidu.com/s/1kOxjQgesr7ML1N_uMHBMCQ 提取码:150a 若链接失效,请联系本人:18642984053@163.com
  • 算法复杂性分析及运算规则证明(一)

    千次阅读 2019-03-07 13:26:03
    我们有必要知道算法的复杂性是算法运行所需要的计算机资源的量,需要空间资源的量称为空间复杂度,同样需要时间资源的量称为时间复杂度。那麽这个量与什么有关系呢? 这个量应该是只依赖于要解决的问题的规模,算法...
  • 领域驱动设计-软件核心复杂性应对之道 一本比较好的领域设计书
  • 算法复杂性和如何计算时间复杂度

    万次阅读 多人点赞 2017-01-17 17:16:13
    算法的复杂性有时间复杂性和空间复杂性之分 通常考虑3种情况下的时间复杂性:最坏,最好和平均情况下的计算复杂性;当然可操作性最好且最有实际价值的是最坏情况下的时间复杂性 T(n)=max(t(i)) 设i是算法A的一个输入...
  • 正则表达式校验密码复杂性的规则

    千次阅读 2019-07-05 11:04:11
    最近做一个项目,需要校验密码复杂性,想了想还是用正则表达式来判断方便快捷; 因密码是数字+字母组成,就只校验数字和字母是否存在; 例: private String checkDataValid(String account, String pwd, String...
  • 控制复杂性是计算机编程的本质

    千次阅读 2019-11-20 11:39:51
  • 算法复杂性分析概述

    千次阅读 2019-04-25 18:03:56
    算法复杂性 = 算法所需要的计算机资源 算法的时间复杂性T(n)=T(n,i); 算法的空间复杂性S(n)= S(n,i) 。 其中n是问题的规模(输入大小)i表示算法及其输入。 算法的时间复杂度反映了程序执行时间随输入规模增长而...
  • 神州数码云平台:Windows Sever 2012 R2更改组策略密码策略为无复杂性要求 作者:我道梦 关注我的CSDN博客,更多笔记知识还在更新! 设置启用组策略中“密码必须符合复杂性要求”后,设置和更改密码时,系统将会...
  • 问题的复杂性小结

    千次阅读 2017-10-29 12:21:13
    0与算法复杂性的区别 1P问题 2NP问题 3NP-Complete问题 4NP-Hard问题 5Non-NP问题 关系图0、与算法复杂性的区别 算法的复杂性是指解决问题的一个具体的算法的执行时间,这是算法的性质。 问题的复杂性是指这个问题...
  • 1.windows +R 输入:gpmc.msc 进入组策略管理界面。...4.禁用密码必须符合复杂性要求,设置密码长度最小值为0个字符(可设置为空),修改密码最短使用期限为0天(密码永远不会过期)。其他默认不变。...
  • 密码复杂性规则

    万次阅读 2017-06-06 05:47:26
     如果使用的是数字和小写字母,那么复杂性是10位数字加上26位小写字母的6次方,即36的6次方。  如果使用数字、小写字母、大写字母,那么复杂性就是10位数字加上26位小写字母加上26位大写字母,即62的6次方。  ...
  • Windows Server 2003解决办法...密码必须符合复杂性要求:由“已启用”改为“已禁用”; 密码长度最小值:由“7个字符”改为“0个字符” 使此策略修改生效有如下方法: 1、等待系统自动刷新组策略,约5分钟~15分...
  • 今天偶然间碰到数学界著名的七大千禧难题,关注了P和NP问题,但是里面提到关于算法的复杂性的描述已经超出了自己的知识范围,我找到了一本书来弥补这个不足,即《计算复杂性与算法分析》,希望自己在看完这本书之后...
  • 其实近似熵就是时间序列下增加一个维度后熵的减少!!!...近似熵和样本熵都是对非稳定时间序列的复杂型的度量,其思想都是检测时间序列中的新的子序列产生概率。 近似熵: 是对数据是否有规...
  • 注:密码的复杂性在生产环境中一定要保留,安全第一,而且要养成习惯 但在非生产环境中,为了实验方便,一般都会取消密码的复杂性。本着实验的目的,这里我就取消了它。 开始,用图说话: 也可以使用下图的方式...
  • 软件系统复杂性(1/2)

    千次阅读 2018-01-05 14:07:25
    1.2节讨论软件系统的复杂性,可以说软件系统比自然界的复杂性还要高,并讨论了导致软件系统复杂的四大原因。1.3节总结了复杂系统的5大属性,读者可以结合1.1节中直观的实例对其进行思考与理解。 摘录:  某些...
  • 造成软件复杂性的原因

    千次阅读 2016-04-04 09:51:34
    软件复杂性的几个原因: 1、问题域的复杂性,造成这种复杂性的主要原因,还是用户与开发者之间的“沟通问题”,用户常常对某个需求只存在一个模糊的概念,对具体要实现成一个什么样子没有特别明显的想法。并且由于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,493,061
精华内容 997,224
关键字:

复杂性

友情链接: haffman.rar