本项目Github地址(同时包括两个做业项目):
Assignment03 -- https://github.com/Oberon-Zheng/SoftwareEngineeringAssignmentsgit
st=>start: Start e=>end: End cond=>condition: Option op1=>operation: solution_1 op2=>operation: solution_2 st->cond cond(yes)->op1->e cond(no)->op2->e
问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。
- Baidu Baikegithub
关于我我的对于此问题的算法考虑我将另起一个博文(会吾倚马万言,易此帜之外链)发布,这里直接使用Kadane算法(使用Python完成的一个子程序,这里改写为C++的)加以实现,关于这个算法的具体实现细节将一样在那篇博客连同我本身的想法一同发布,这里先给出这个程序的流程图(根据mermaid语法绘出):算法
I'll be your mermaid, caught on your rod
- Memorized Mermaid
-ScottSkott小程序
注意,因为cnblogs的mermaid组件须要通过google的code analytics服务器,所以在转换为正确的流程图以前须要花费一段时间
So, stay tuned, plz!
我有雅各布的天梯,我天下无敌,我藐视银河系
ESO黑洞警告数组
上述程序可以求得关于传入序列arry
的一个最大子列和(下文简称最大和),而且可以获得这个最大子列在arry中的位置。服务器
本案例以C++语言加以实现,代码以下:dom
///File : MaxSubarray.h #pragma once #include <vector> using namespace std; struct RangeBlock { int sum; int start; int end; }; /// File : MaxSubarray.c #include "MaxSubarray.h" #define POSCLIP(X) (X>0)?X:0 #define MAX(X,Y) (X>Y)?X:Y using namespace std; RangeBlock MaxSubArray(vector<int> arry) { RangeBlock rb = { 0,-1,-1 }; if (arry.size() == 0) { return rb; } int maxsum = 0; int sum = 0; int kickoff = 0; for (int i = 0; i < arry.size(); i++) { sum = POSCLIP(sum + arry[i]); maxsum = MAX(sum, maxsum); if (sum == 0 && arry[i] < 0) { kickoff = i + 1; //rb.end = rb.start; } else if(sum == maxsum) { rb.start = kickoff; rb.end = i; } } rb.sum = maxsum; return rb; }
这个程序传入一个标准库vector<int>
容器做为输入(之因此使用vector
是由于vector
的长度可变),而后经过一个RangeBlock
型对象返回最大子列的和与区间。 根据上面的流程图,这里面存在一个长度不定的循环,而且在循环内仍然有一个分支,所以这里面想要对循环内的全部可能状况作出测试是不可能的,所以for
内的测试仅对若干种条件组合(每一个数据会分别将覆盖内部的三个分支)。oop
本单元测试将知足断定-条件覆盖(全部的断定都走一次,全部的条件分两个分支都知足一次) 概括上述代码能够获得以下的条件断定列表:单元测试
arry.size()==0 <---> arry.size()!=0 --[1] i < arry.size() <---> i >= arry.size() --[2] sum==0 <--->sum!=0 --[3] arry[i]<0 <---> arry[i] >=0 --[4] sum == maxsum <---> sum != maxsum --[5]
根据代码的执行状况来看,其中[3]和[4]存在条件组合,实际上当[3]的左命题成立时,[4]的右命题必定不成立,同时若[2]右、[3]右、[4]左成立则[5]左必定不成立(arry[i]<0,maxsum>=sum ==>> sum+arry[i]<maxsum
),而[2]的命题在执行一句for循环的时候总会有成立和不成立(维持循环和跳出循环),对于上述推断为必定不成立的关系下,没法找出具体的测试用例(由于是逻辑关系制约的不可能发生,好比我没法找到sum==0 && arry[i]>0
的测试数据,由于根本写不出来,除非遭到某种硬件错误致使arry[i]
或者sum
发生翻转,但这不是单元测试可以解决的),对于for的测试主要聚焦于OBOE问题(一步之差),而当[1]的前命题成立时,实际上以后的条件实际上都走不到,但若是必要考虑后面的条件的话,实际上只能讨论i>=arry.size()
,for循环实际上也执行不了,所以咱们概括出以下的条件组合:测试
arry.size() == 0 --[S1] arry.size() != 0 && sum == 0 && arry[i]<0 && sum == maxsum --[S2] arry.size() != 0 && sum == 0 && arry[i]<0 && sum != maxsum --[S3] array.size() != 0 && sum != 0 && arry[i] < 0 && sum != maxsum --[S4] array.size() != 0 && sum != 0 && arry[i] >= 0 && sum == maxsum --[S5] array.size() != 0 && sum != 0 && arry[i] >= 0 && sum != maxsum --[S6]
单元测试将围绕[S1]到[S6]产生测试数据以下:
S1 | S2 | S3 | S4 | S5 | S6 |
---|---|---|---|---|---|
空数组 | 全负数组 | 第一个负数 | 有正数的时候加入负数 | 加入非负数(开始) | 加入非负数(继续加入) |
根据上述概括,编订了以下的测试数据:
#include "stdafx.h" #include "CppUnitTest.h" #include "..\Assignment03\MaxSubarray.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace UnitTestA03 { TEST_CLASS(UnitTest1) { public: TEST_METHOD(TestEmpty) //S1 { std::vector<int> a; RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 0); Assert::AreEqual(rb.start, -1); Assert::AreEqual(rb.end, -1); } TEST_METHOD(TestAllNegative) //S2 { std::vector<int> a = { -1,-5,-8,-10,-6,-40,-1630,-5 }; RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 0); Assert::AreEqual(rb.start, -1); Assert::AreEqual(rb.end, -1); } TEST_METHOD(TestAllZero) //S4 { std::vector<int> a(10, 0); RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 0); Assert::AreEqual(rb.start, 0); Assert::AreEqual(rb.end, 9); } TEST_METHOD(TestOscillating) // S3和S5 { std::vector<int> a = {0, 1,-2,3,-4,5,-6,7 }; RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 7); Assert::AreEqual(rb.start, 7); Assert::AreEqual(rb.end, 7); } TEST_METHOD(TestInhaling) // S4和S5 { std::vector<int> a = {1, 3,-2,-3,0,0,-6,-10 }; RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 4); Assert::AreEqual(rb.start, 0); Assert::AreEqual(rb.end, 1); } TEST_METHOD(TestNormal) //综合测试 { std::vector<int> a = { 1,3,2,-4,0,5,8,-1,-10 }; RangeBlock rb = MaxSubArray(a); Assert::AreEqual(rb.sum, 15); Assert::AreEqual(rb.start, 0); Assert::AreEqual(rb.end, 6); } }; }
最终各测试均经过:
Kadane算法在$\omicron(n+k)$的复杂度下获得了正确的结果。
下表为某商场每日营业额与应收税率的对照表,请编写一小程序根据该商场每日营业额计算其实际应缴纳税费。
营业额X/¥ 税率 1000≤X<5000 5% 5000 ≤X<10000 8% X≥10000 10%
这个项目使用C#及其单元测试功能。
(为何不用C++呢,emmmm……由于玩的就是花里胡哨!)
这个项目相对比较简单,这里直接给出代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Assignment03_2 { public class Business { private float turnover; public static Dictionary<float, float> TaxRate = new Dictionary<float, float> { { 0.00f, 0.00f }, { 1000.00f, 0.05f }, { 5000.00f, 0.08f }, { 10000.00f, 0.10f } }; public float Turnover { get { return turnover; } set { turnover = value; } } public float Tax { get { if(float.IsNaN(turnover) || float.IsInfinity(turnover) || turnover < 0.00f) { throw new Exception("Invalid turnover"); } else { if(turnover < 1000.00f) { return 0.00f; } else if(turnover < 5000.00f) { return (turnover - 1000.00f) * 0.05f; } else if(turnover < 10000.00f) { return 200 + (turnover - 5000.00f) * 0.08f; } else { return 200 + 400 + (turnover - 10000.00f) * 0.10f; } } } } public Business() { turnover = 0; } public Business(float turnover) { this.turnover = turnover; } } }
分析上述代码发现,这里面的条件分支主要是围绕浮点型的turnover
字段展开的。 关于浮点型,首先有税率表中的取值划分之外,float型还存在负取值以及float.NaN
和float.PositiveInfinity
和float.NegativeInfinity
这些取值,基于这些考虑,单元测试所使用的数据也必须涵盖这些可能的范围,所以给出单元测试程序以下:
using Microsoft.VisualStudio.TestTools.UnitTesting; using Assignment03_2; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Assignment03_2.Tests { [TestClass()] public class BusinessTests { [TestMethod()] public void TestInvalid() { Business b = new Business(float.NaN); try { float t = b.Tax; } catch(Exception e) { Assert.AreEqual(e.Message, "Invalid turnover"); } b.Turnover = float.NegativeInfinity; try { float t = b.Tax; } catch (Exception e) { Assert.AreEqual(e.Message, "Invalid turnover"); } b.Turnover = float.PositiveInfinity; try { float t = b.Tax; } catch (Exception e) { Assert.AreEqual(e.Message, "Invalid turnover"); } b.Turnover = -5000.00f; try { float t = b.Tax; } catch (Exception e) { Assert.AreEqual(e.Message, "Invalid turnover"); } } public void TestTax() { Business b = new Business(); b.Turnover = 500.00f; Assert.AreEqual(b.Tax, 0.00f, float.Epsilon); b.Turnover = 1000.00f; Assert.AreEqual(b.Tax, 0.00f, float.Epsilon); b.Turnover = 2500.00f; Assert.AreEqual(b.Tax, 75.00f, float.Epsilon); b.Turnover = 5000.00f; Assert.AreEqual(b.Tax, 200.00f, float.Epsilon); b.Turnover = 7500.00f; Assert.AreEqual(b.Tax, 400.00f, float.Epsilon); b.Turnover = 10000.00f; Assert.AreEqual(b.Tax, 600.00f, float.Epsilon); b.Turnover = 20000.00f; Assert.AreEqual(b.Tax, 1600.00f, float.Epsilon); } } }
最终,各项测试所有经过