65.9K
CodeProject 正在变化。 阅读更多。
Home

Edumatter-814:学校数学计算器和方程求解器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2012 年 10 月 2 日

CPOL

14分钟阅读

viewsIcon

79851

面向 Microsoft Windows® 7/8 的教育软件套装,包含“五合一”学校数学计算器和方程求解器,面向学生和教育工作者

项目状态

该项目(最初命名为“Edumatter® M-12”)The Windows 8* & Ultrabook™ App Innovation Contest (AIC-2012) 的获奖作品。

由 Dr. Alexander Bell (Infosoft International Inc, NY) 开发的面向 Microsoft Windows® 的教育软件套装“Edumatter® M-12”(或简称 Edumatter)面向公立/私立学校(K-12)和大学的学生、教育工作者以及自学/家庭教育者。

该应用程序软件套装(“捆绑包”)包含 2 个核心应用程序和 1 个可选的应用扩展

  • 数学计算器和方程求解器 Edumatter M12-L “精简版”(核心应用 [1],AppUp® 下载)
  • 数学计算器和方程求解器 Edumatter M-12 “标准版”(核心应用 [2],AppUp® 下载)

核心应用程序(即 Edumatter M-12/M12-L)可以在配备 Microsoft Windows OS 7/8(任何版本)的任何硬件平台上运行,支持常规桌面“鼠标/键盘”和/或触摸屏模式。用户可以下载免费的“精简版”或价格合理的“标准版”:后者包含基本的教师搜索选项。

应用程序扩展(“FindTutor”)可以作为独立的桌面应用程序运行,扩展核心应用程序的功能。它针对 Intel 公司推出的新款 Ultrabook™ 产品线进行了优化,需要 Windows 8(任何版本)和已启用的客户端计算机上的 GPS/地理定位功能,以便在交互式在线 Microsoft Bing 地图中提供复杂的教师搜索选项(相关技术在 Codeproject 文章 [8] 中有部分描述)。

重要提示:核心应用程序(Edumatter M-12/M12-L)非常适合广泛的受众和各个年龄段。应用程序扩展“FindTutor”(该扩展除了其他功能外,还实现了实时 GPS/地理位置信息检索)由于隐私保护方面的考虑,主要面向年龄稍大的用户。有关这一非常重要的问题的更多详细信息,请参阅“用户手册”和“最终用户许可协议”。

类别 1:教育

类别 2:生产力

平台:适用于 Win 7/8(任何版本)的桌面应用

上述软件套装(“捆绑包”)中包含的所有应用程序均可在 Intel 的 AppUp 商店 [1...3] 中找到。本文档中包含示例屏幕截图,也可在 [4] 中以幻灯片放映的形式查看。

Ultrabook 功能在应用程序中的应用

  • 触摸屏和键盘/鼠标操作模式
  • 地理位置/GPS 传感器,用于搜索目标/本地区域的数学辅导教师、SAT、ACT、GED 备考等。
  • 多核 CPU(i3/i5/i7),用于实现高效的并行算法,特别是素数分解。
  • 多模式语音增强型 UI,利用 Ultrabooks 的高响应性,通过语音消息TTS(文本转语音)技术向用户提供信息。
其他卖点
  • 能够处理分数、带分数、小数和整数的任意组合的独特能力;结果同时显示为分数和小数。
  • 快速的素数分解算法,能够分解多达 18 位数的整数。
  • 通用数学运算、素数分解和并行处理算法的计算效率基准测试,尤其是在由 Intel 多核 CPU 驱动的硬件平台上。

背景

核心应用程序(Edumatter M-12/M12-L) 配置为模块化的“五合一”学校数学计算器和方程求解器软件套装,包含以下 5 个模块

  • FC12 分数计算器
  • PF12 素数分解计算器
  • LE12 线性方程求解器
  • SE12 线性方程组求解器
  • QE12 二次方程求解器

图 1:Edumatter M12 核心应用,示例屏幕截图

每个模块都提供了处理任何分数(真分数/假分数)、整数或带分数以及小数的独特能力。计算结果也以分数和小数形式显示,但二次方程求解器 (QE12) 除外,它仅以小数形式输出实数或复数(原因显而易见,后者根可能为无理数)。

分数计算器 FC12

分数计算器 FC12 扩展了流行的在线多分数计算器 [7] 的功能(注意:其在线对应产品多年来一直位居 Google 搜索列表前列)。在线提供专门的幻灯片演示 [4]。编码解决方案已在 Codeproject 文章 [5,6] 中部分讨论。

FC-12 可以对整数和带分数、分数(真分数/假分数)以及小数(正/负)的任何组合执行二元和一元算术运算,例如:4、8.75、2/3、9/4、3 5/7、-6 2/3、9、8.75、-12。任何分数或带分数都可以约分到最简形式。任何假分数都可以转换为带分数,反之亦然。作为额外的便利功能,所有计算结果都显示在“历史记录”文本框中,因此可以复制到计算机的“剪贴板”内存中,或存储在标准文本文件中以供将来使用。

图 2:分数计算器 FC-12,示例屏幕截图

素数分解计算器 PF12

在此提醒,素数分解是指找出任何正整数(整数)的非平凡因子,其中任何整数的平凡因子仅为 1 和该数本身。任何其他因子都被视为非平凡因子,例如,数字 6 有两个非平凡因子:6 = 2*3。

素数分解计算器 PF12 实现了一个有效的计算算法,可以快速地对多达18 位数(!)的任何整数进行素数分解,这在任何市售学校数学计算器中都是无与伦比的。结果显示在计算历史文本框中,也可以复制到计算机的“剪贴板”内存中,或存储在标准文本文件中以供将来使用。

图 3:素数分解计算器 PF-12,示例屏幕截图

线性方程求解器 LE12

在此提醒,线性方程的标准形式可以写成
AX + B = C,
其中 A、B 和 C 是方程的系数。线性方程的通解为:X = (C-B)/A

线性方程求解器 LE12 能够求解具有分数或整数系数的线性方程。结果以分数和等效小数的形式显示在计算历史文本框中,也可以复制到计算机的“剪贴板”内存中,或存储在标准文本文件中以供将来使用。

线性方程组求解器 SE12

在此提醒,线性方程组的标准形式可以写成
A1X + B1Y= C1
A2X + B2Y= C2

线性方程组求解器 SE12 能够求解具有分数、小数或整数系数的线性方程组(参见本文档 [1] 中包含的示例屏幕截图 #4)。结果以分数和等效小数的形式显示在计算历史文本框中,也可以复制到计算机的“剪贴板”内存中,或存储在标准文本文件中以供将来使用。

图 4:线性方程组求解器 SE-12,示例屏幕截图

二次方程求解器 QE12

在此提醒,二次方程的标准形式可以写成:Ax^2+Bx+C=0。

二次方程求解器 QE12 能够求解具有分数或整数系数的二次方程。其输出可以是实数或复数,仅以小数形式呈现:正如我们从理论中知道的(参见文章中推荐的链接),二次方程的解可能是无理数,因此无法用有限分数表示。与之前所有情况一样,计算结果会显示在历史框中,可以复制到计算机的“剪贴板”中,或存储在标准文本文件中。

图 5:二次方程求解器 QE-12,示例屏幕截图

使用代码

分数计算器 FC12 中实现的核心算法此前已在 Codeproject [5,6] 的提示部分发布和讨论过。分数算术使用了一种新颖的计算引擎,通过两个相互关联的类(Fraction 和 Mixed Number)扩展了现有的数值类型。后者是一种最通用的类型,结合了整数和分数部分。下面的代码片段(列表 1.)演示了 Fraction 类的实现细节,包括其全面的错误处理、运算符重载和方法覆盖技术。

列表 1. Fraction 类的实现细节
    #region private Vars
    private Int64 _n; // var containing Numerator
    private Int64 _d; // var containing Denominator (positive num)
    #endregion

    #region private vars: error msg
    private const string errMsgDivBy0 = "Division by 0";
    private const string errMsgDenom = "Denominator must be >0";
    private const string errMsgNotFraction = "Not a valid Fraction";
    private const string errMsgOutOfRange = "Number out of range";
    private const string errMsgStackOverflow = "Stack Overflow";
    private const string errMsgArithmetic = "Arithmetic error";
    #endregion

    #region public properties: Numerator and Denominator
    /// <summary>Numerator: Int64</summary>
    internal Int64 Numerator 
    { 
        get { return _n; } 
        set {_n = checked(value);} 
    }

    /// <summary>Numerator: Int64 (>0)</summary>
    internal Int64 Denominator 
    { 
        get { return _d; } 
        set 
        {
            if (value <= 0) { throw (new ArgumentException(errMsgDenom)); }
            else{ _d = checked(value); }
        }
    }
    #endregion

    #region public ctor:  Fraction(Int64 N, Int64 D)
    /// <summary>ctor: parameterless</summary>
    internal Fraction() { }

    /// <summary>ctor w/parameters</summary>
    /// <param name="N">Int64</param>
    /// <param name="D">Int64; must be >0</param>
    internal Fraction(Int64 N, Int64 D)
    {
        _n = checked(N);
        if (D <= 0) { throw (new ArgumentException(errMsgDenom)); }
        else { _d = checked(D); }
    }
    #endregion

    #region Fraction.ToString() override method
    /// <summary>
    /// Fraction.ToString() override method
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        try
        {
            if (_n == 0) return "0";
            if (_d == 1) return _n.ToString();
            return (_n.ToString() + "/" + _d.ToString());
        }
        catch (ArgumentOutOfRangeException) { throw new ArgumentOutOfRangeException(errMsgOutOfRange); }
        catch (DivideByZeroException) { throw new DivideByZeroException(errMsgDivBy0); }
        catch (StackOverflowException) { throw new StackOverflowException(errMsgStackOverflow); }
        catch (ArithmeticException) { throw new ArithmeticException(errMsgArithmetic); }
        catch (ArgumentException) { throw new ArgumentException(errMsgNotFraction); }
        catch { throw; }
    }
    #endregion

    #region Fractions Arithmetic: Operators overload
    /// <summary>
    /// Add 2 Fractions, use LCM
    /// </summary>
    /// <param name="Fraction1">Fraction</param>
    /// <param name="Fraction2">Fraction</param>
    /// <returns>Fraction</returns>
    public static Fraction operator +(Fraction Fraction1, Fraction Fraction2)
    {
        Fraction ret = new Fraction();
        Int64 _lcm;
            
        // validate denominator for 0
        if (Fraction1.Denominator == 0 || Fraction2.Denominator == 0)
        { throw (new DivideByZeroException(errMsgDivBy0)); }

        // validate denominator for negatives
        if (Fraction1.Denominator < 0 || Fraction2.Denominator < 0)
        { throw (new ArgumentException(errMsgDenom)); }

        try
        {
            // find LCM of two denominators
            _lcm = checked (Integers.LCM(Fraction1.Denominator, Fraction2.Denominator));

            // calculate numerator using LCM values
            ret.Numerator = 
                checked((Int64)(checked (Fraction1.Numerator * (Int64)(_lcm/Fraction1.Denominator)) +
                checked (Fraction2.Numerator * (Int64)(_lcm/Fraction2.Denominator))));

            // set resulting denominator to LCM
            ret.Denominator = _lcm;

            // return fraction
            return ret;
        }
        catch (ArgumentOutOfRangeException) { throw new ArgumentOutOfRangeException(errMsgOutOfRange); }
        catch (DivideByZeroException) { throw new DivideByZeroException(errMsgDivBy0); }
        catch (StackOverflowException) { throw new StackOverflowException(errMsgStackOverflow); }
        catch (ArithmeticException) { throw new ArithmeticException(errMsgArithmetic); }
        catch (ArgumentException) { throw new ArgumentException(errMsgNotFraction); }
        catch { throw; }
        finally { ret = null; }
    }
    #endregion 

 

素数分解 PF-12 模块实现了一种新颖的并行算法,有效地利用了 Ultrabooks 多核 CPU(如 i3/i5/i7)的强大功能来加速素数因子的计算。下面的代码片段(列表 2)演示了“查找第一个非平凡因子”算法的并行实现,与串行实现相比,在分解大素数的情况下,性能有了显著提高。

列表 2. 查找整数(Int64)的第一个非平凡因子的快速并行算法
#region GetFirstFactorParallel(Int64 Num) algorithm
/// <summary>
/// ===================================================================
/// Copyright(C)2012 Alexander Bell
/// ===================================================================
/// parallel algorithm accepts Int64 Num 
/// and returns either first found not-trivial factor, or number 1.
/// Note: not-trivial factor can be relatively small composite number,
/// thus it requires subsequent factoring of itself, adding very 
/// small overhead, because its value is far less than SQRT(Num)
/// This algo provides significant performance boost 
/// in comparison to serial implementation while prime factoring
/// big prime numbers, like for e.g. 18-digit primes:
/// 999999999999999989
/// 999999999999999967
/// 999999999999999877
/// 999999999999999863
/// 999999999999999829
/// or 17-digit primes:
/// 99999999999999997
/// 99999999999999977
/// 99999999999999961
/// 99999999999999943
/// 99999999999999919
/// </summary>
/// <param name="Num">Int64</param>
/// <returns>Int64</returns>
internal static Int64 GetFirstFactorParallel(Int64 Num)
{
    // use concurrent stack to store non-trivial factor if found
    ConcurrentStack<Int64> _stack = new ConcurrentStack<Int64>();
            
    // object to specify degrees of parallelism
    ParallelOptions _po = new ParallelOptions();

    try
    {
        // return value initially set to 1
        Int64 _ret = 1;

        // step 1: try to factor on base 2, return if OK
        if (Num % 2 == 0) return 2;

        // step 2: try to factor on base 3, return if OK
        if (Num % 3 == 0) return 3;

        #region parallel algo to find first non-trivial factor if exists

        // set upper limit
        Int64 _upMargin = (Int64)Math.Sqrt(Num) + 1;

        // number of CPU cores
        int _countCPU = System.Environment.ProcessorCount;

        // max degree of parallelism set equal to _cpuCount
        _po.MaxDegreeOfParallelism = _countCPU;

        Parallel.For(0, 2, _po, (i, _plState) =>
        {
            // starting number for inner loops (5 and 7)
            int _seed = 5 + 2 * i;

            // inner loops running in parallel;
            // notice that because input Num was already tested for factors 2 and 3,
            // then increment of 6 is used to speed up the processing,
            // thus in dual core CPU it looks like:
            // 5, 11, 17, 23, 29, etc. in first thread
            // 7, 13, 19, 25, 31, etc, in second thread
            for (Int64 j = _seed; j < _upMargin; j += 6)
            {
                // exit loop if stack contains value
                if (_stack.Count != 0) { break; }

                // check divisibility
                if (Num % j == 0)
                {
                    // push non-trivial factor to ConcurrentStack and exit loop
                    if (_stack.Count == 0) { _stack.Push(j); }
                    break;
                }
            }
        });
        #endregion

        // return the value in ConcurrentStack if exists, or 1
        return (_stack.TryPop(out _ret)) ? _ret : 1;
    }
    catch { throw; }
    finally { _po = null; _stack = null; }

}
#endregion

重要提示:此并行算法需要库引用

using System.Collections.Concurrent;
using System.Threading.Tasks;

对于较小的整数(小于 10 位数),由于上述并行算法的线程同步和控制逻辑开销,串行实现可能比并行算法提供更好的性能。下面的代码片段(列表 3)演示了高效素数分解算法的串行实现。

列表 3. 整数(Int64)的快速素数分解算法,串行实现
#region PrimeFactoringSerial(Int64 Num) algorithm
/// <summary>
/// =======================================================
/// Copyright(C)2012 Alexander Bell
/// =======================================================
/// get list of prime factors of Int64 Num in serial mode
/// </summary>
/// <param name="Num">Int64</param>
/// <returns>List<Int64></returns>
internal static List<Int64> PrimeFactoringSerial(Int64 Num)
{
    // list of Prime Factors to return
    List<Int64> _lstFactors = new List<Int64>();

    // temp var to store intermediate result (_num/factors)
    Int64 _num = Num; 

    #region factorize on base 2
    // divide by mod 2, add to the list if successful,
    // try again on new number (_num / 2)
    while (_num % 2 == 0)
    {
        _lstFactors.Add(2);
        _num = (Int64)(_num / 2);
    }

    // if _num==1 then stop factoring and return
    if (_num == 1) { return _lstFactors; }
    #endregion

    #region try factoring in range: 3 ... Math.Sqrt(_num) + 1;
    // set initial upper limit
    Int64 _upMargin = (Int64)Math.Sqrt(_num) + 1;

    // loop with variable upper limit;
    // notice increment of 2 to loop through odd numbers only
    for (Int64 i=3; i<=_upMargin; i+=2)
    {
        if (_num % i == 0)
        {
            // add prime factor to the List
            _lstFactors.Add(i);

            // get new integer to factorize (_num)
            _num = (Int64) (_num / i);

            // calculate the loop upper limit corresponding to new _num
            _upMargin = (Int64) Math.Sqrt(_num) + 1;

            // keep value of (i) the same in next iteration
            i-=2;
        }
    }

    // add the last factor
    _lstFactors.Add(_num);

    // sort list
    _lstFactors.Sort();

    return _lstFactors;
    #endregion
}
#endregion  

FindTutor-2013 应用程序扩展实现了一个利用 Microsoft Bing 地图技术的解决方案,该技术在 Codeproject 文章 [8] 中有所描述。

地理位置/GPS 功能和自定义地图搜索

FindTutor-2013 应用程序利用 Ultrabooks 的新颖地理位置/GPS 功能,该功能允许在实时模式下检索地理坐标(纬度、经度和可选精度)。这些坐标用于确定 POI(兴趣点)搜索区域的中心(应用程序还允许手动输入上述坐标)。为了获得一组 POI 并将它们显示为搜索区域中的标准/自定义图钉,其中心点的坐标被编码在 Web 查询请求中,发送到其在线 Web 对应方,该对应方由 Microsoft Bing 地图技术 [8,9] 提供支持。

Microsoft Bing 地图实现了其自己的标准“查找内容”功能,该功能允许在由其中心点指定的搜索区域内查找 POI [9]。此标准化的 Bing 地图搜索通过基于专有 POI 数据库和大圆(航线)距离计算模块 [10,11] 的自定义搜索引擎进行了扩展。相应的 POI 以不同样式和大小的自定义图钉形式显示在地图上。

上述引擎的核心方法执行地球上两个地理点(分别由其纬度 Lati1、Lon1 和 Lati2、Lon2 指定)之间的大圆距离计算:有许多计算方法在各种在线资源(例如 [12, 13])中都有描述。一种流行的方法基于所谓的Haversine 公式。下面的代码片段(列表 4.)演示了该公式的加速 C# 实现。

列表 4. Haversine 公式,用于计算地球上两点之间的距离
    #region private: const
    private const double _radiusEarthMiles = 3959;
    private const double _radiusEarthKM = 6371;
    private const double _m2km = 1.60934;
    private const double _toRad = Math.PI / 180;
    #endregion

    /// <summary>
    /// Haversine formula to calculate 
    /// great-circle (orthodromic) distance on Earth 
    /// High Accuracy, Medium speed
    /// </summary>
    /// <param name="Lat1">double: 1st point Latitude</param>
    /// <param name="Lon1">double: 1st point Longitude</param>
    /// <param name="Lat2">double: 2nd point Latitude</param>
    /// <param name="Lon2">double: 2nd point Longitude</param>
    /// <returns>double: distance in miles</returns>
    public static double DistanceMilesHaversine(double Lat1,
                                                double Lon1,
                                                double Lat2,
                                                double Lon2)
    {
        try
        {
            double _radLat1 = Lat1 * _toRad;
            double _radLat2 = Lat2 * _toRad;
            double _dLatHalf = (_radLat2 - _radLat1) / 2;
            double _dLonHalf = Math.PI * (Lon2 - Lon1) / 360;

            // intermediate result
            double _a = Math.Sin(_dLatHalf);
            _a *= _a;

            // intermediate result
            double _b = Math.Sin(_dLonHalf);
            _b *= _b * Math.Cos(_radLat1) * Math.Cos(_radLat2);

            // central angle, aka arc segment angular distance
            double _centralAngle = 2 * Math.Atan2(Math.Sqrt(_a + _b), Math.Sqrt(1 - _a - _b));

            // great-circle (orthodromic) distance on Earth between 2 points
            return _radiusEarthMiles * _centralAngle;
        }
        catch { throw; }
    }

请注意,大圆距离的计算本质上需要找到中心角(_centralAngle);将其乘以地球半径(_radiusEarthMiles = 3959,或 _radiusEarthKM = 6371)将得到所述距离。

另一个著名的公式,如 [14] 中所述,利用球面余弦定理,可以实现更简化的编码技术:计算中心角 (_centralAngle) 只需要一行代码。下面的代码片段(列表 5)提供了该公式的加速 C# 实现。

列表 5. 球面余弦定理,用于计算地球上两点之间的距离
    /// <summary>
    /// Spherical Law of Cosines formula to calculate 
    /// great-circle (orthodromic) distance on Earth;
    /// High Accuracy, Medium speed
    /// http://en.wikipedia.org/wiki/Spherical_law_of_cosines
    /// </summary>
    /// <param name="Lat1">double: 1st point Latitude</param>
    /// <param name="Lon1">double: 1st point Longitude</param>
    /// <param name="Lat2">double: 2nd point Latitude</param>
    /// <param name="Lon2">double: 2nd point Longitude</param>
    /// <returns>double: distance in miles</returns>
    public static double DistanceMilesSLC(  double Lat1, 
                                            double Lon1, 
                                            double Lat2, 
                                            double Lon2)
    {
        try
        {
            double _radLat1 = Lat1 * _toRad;
            double _radLat2 = Lat2 * _toRad;
            double _radLon1 = Lon1 * _toRad;
            double _radLon2 = Lon2 * _toRad;

            // central angle, aka arc segment angular distance
            double _centralAngle = Math.Acos(Math.Sin(_radLat1) * Math.Sin(_radLat2) +
                    Math.Cos(_radLat1) * Math.Cos(_radLat2) * Math.Cos(_radLon2 - _radLon1));

            // great-circle (orthodromic) distance on Earth between 2 points
            return _radiusEarthMiles * _centralAngle;
        }
        catch { throw; }
    }

对于相对较小的搜索区域(半径 <200 英里),通过使用球面投影到平面的公式(如 [11] 中所述)并应用勾股定理,可以实现高精度和高计算速度,因为三角函数的使用最少。下面的代码片段(列表 5)演示了该算法的加速 C# 实现,该算法被选用于自定义地理搜索引擎(FindTutor-2013 应用的一部分)。

列表 5. 使用球面投影公式计算大圆距离
    /// <summary>
    /// Spherical Earth projection to a plane formula (using Pythagorean Theorem)
    /// to calculate great-circle (orthodromic) distance on Earth.
    /// http://en.wikipedia.org/wiki/Geographical_distance
    /// central angle = 
    /// Sqrt((_radLat2 - _radLat1)^2 + (Cos((_radLat1 + _radLat2)/2) * (Lon2 - Lon1))^2)
    /// Medium Accuracy, Fast,
    /// relative error less than 0.1% in search area smaller than 250 miles
    /// </summary>
    /// <param name="Lat1">double: 1st point Latitude</param>
    /// <param name="Lon1">double: 1st point Longitude</param>
    /// <param name="Lat2">double: 2nd point Latitude</param>
    /// <param name="Lon2">double: 2nd point Longitude</param>
    /// <returns>double: distance in miles</returns>
    public static double DistanceMilesSEP(double Lat1,
                                          double Lon1,
                                          double Lat2,
                                          double Lon2)
    {
        try
        {
            double _radLat1 = Lat1 * _toRad;
            double _radLat2 = Lat2 * _toRad;
            double _dLat = (_radLat2 - _radLat1);
            double _dLon = (Lon2 - Lon1) * _toRad;

            double _a = (_dLon) * Math.Cos((_radLat1 + _radLat2) / 2);

            // central angle, aka arc segment angular distance
            double _centralAngle = Math.Sqrt(_a * _a + _dLat * _dLat);

            // great-circle (orthodromic) distance on Earth between 2 points
            return _radiusEarthMiles * _centralAngle;
        }
        catch { throw; }
    }
语音增强(TTS)

用户界面 (UI) 实现了可选的语音增强功能,通过TTS(文本转语音)技术提供语音说明,用户可以开启/关闭此选项。

基准测试

核心应用程序 Edumatter M-12/M12-L 包含 3 个专有性能基准测试,提供客户端计算机的计算效率估算,即

  • 通用数学
  • 素数分解
  • Intel i3i5i7 多核 CPU 架构上的并行处理算法效率

关注点

产品可用性和兼容性

核心应用程序“Edumatter M-12/M12-L”完全兼容 Microsoft Windows 7 或 8,并且针对 Ultrabooks™(Intel 启发)硬件平台进行了优化。它向后兼容以前的 Windows Vista/XP 版本。

语音技术

Edumatter M-12/M12-L 核心应用程序和应用程序扩展 FindTutor 实现了多模式语音增强型用户界面,利用 Microsoft Speech 对象库,特别是 TTS(文本转语音)技术集,为用户提供语音说明和错误消息。根据实际测试,发现 TTS 受益于 Ultrabooks 高内部数据速率(由于复杂的双 SSD/SATA),显著降低了语音响应延迟,提高了整体 UI 响应速度。

数字签名 Microsoft Visual Studio 2010 可执行文件 (.exe) 和安装程序 (.msi) 文件

为 Visual Studio 输出文件添加数字签名(使用从证书颁发机构 (CA) 获取的数字证书)已被证明并非易事。此问题不仅与特定 CA(在本例中为 Comodo [15])有关,而且通常与 Visual Studio 项目有关(版本 2010 用于生成可安装 .msi 文件的 Setup 项目)。标准且最直观的“阻力最小的方法”是转到Project->Properties -> Signing 选项卡,然后仅输入强名称密钥文件(例如,从 CA 收到的CertificateFile.pfx),但这会因错误消息“无法导入以下密钥文件:CertificateFile.pfx。密钥文件可能受密码保护。要解决此问题,请尝试再次导入证书或使用以下密钥容器名称手动将证书安装到强名称 CSP...”而失败。显然,文件CertificateFile.pfx 是并且必须受到保护。因此,经过数小时的 Google 搜索和多次“试错”尝试后,很明显,最可靠和直接的签名 VS 项目输出(.exe 和/或 .msi)的方法之一是使用添加到生成后事件的命令行 [16],该命令行利用signtool.exe(后者包含在 Microsoft SDK [17] 中,可免费下载),如下面的示例所示。

在 VS 2010 中使用 signtool.exe 和命令行进行 .exe 文件数字签名

在 Visual Studio 2010 项目中(相同的技术也适用于 VS 2012),打开Properties->Build Events 选项卡。在“Post-build event command line”文本框中,添加命令,该命令应如下所示(注意命令指向“obj”目录,而不是“bin”)。

"%ProgramFiles%\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe" sign /n [Company Name] /f C:\CA\CertificateFile.pfx /p password /t http://timestamp.comodoca.com/authenticode "$(ProjectDir)obj/x86/Release/YourProduct.exe"
在 VS 2010 中使用 signtool.exe 和命令提示符进行 .msi 文件数字签名

在 Visual Studio 2010 Setup 项目中,在解决方案资源管理器中找到“PostBuildEvent”项,然后输入类似于下面所示的命令行。

"%ProgramFiles%\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe" sign /n "Infosoft Intl Inc" /f C:\CA\CertificateFile.pfx /p password /t http://timestamp.comodoca.com/authenticode "$(ProjectDir)Release/Setup.msi"

请注意,[公司名称](如上面示例中的“Infosoft Intl Inc”)仅为可选参数,可以省略。这些示例中的TimeStamp 服务器对应于上述 CA Comodo [15];证书文件名/位置 (C:\CA\CerticateFile.pfx) 仅为演示目的。

ALS 传感器用例

核心应用程序 Edumatter M-12/M12-L 具有自动样式选择的潜力,例如,根据部分高端 Ultrabooks 所含的环境光传感器 (ALS) 的数据流在高/低对比度配色方案之间切换。可能的实现将需要某种程序上的“带滞后的比较器”(在电气工程中是众所周知的),它会观察 ALS 数据流并根据预定义的亮度阈值自动在样式之间切换。经过认真设计考虑和部分原型测试,发现此类自动控制功能在这种类型的应用程序中是不必要的:“非理性繁荣”,即传感器用例的人工过度延伸,可能会导致持续的“样式抖动”,从而增加烦恼/眼睛刺激,并降低整体用户体验,而不是改善它。或者,样式选择功能被实现为一个简单的菜单选项(有关详细信息,请参阅手册)。

实际设计考虑

如上所述,核心应用程序(Edumatter M12/M12-L)非常适合广泛的受众和各个年龄段。相反,应用程序扩展“FindTutor-2013”(该扩展除了其他功能外,还实现了实时 GPS/地理位置信息检索,这可能被解释为“个人身份信息”,因此被视为敏感信息)由于隐私保护方面的考虑,主要面向年龄稍大的用户。请参阅“用户手册”和“最终用户许可协议”(EULA)以获取有关此非常重要问题的更多详细信息,特别是 EULA 中的“隐私政策”声明。

图 6:Ultrabooks 的实时地理定位功能

图 7:显示附近辅导教师的 Bing 地图网页的示例屏幕截图

在线计算器

如前所述,分数计算器 FC12 有一个功能受限的免费在线对应版本,已置于公共领域。与此应用程序相关的其他计算模块也可在webinfocentral.com 在线获得。比较“两全其美”的优缺点,即桌面应用程序与在线 Web 应用程序:作为 Microsoft Windows 的常规桌面应用程序实现的Edumatter M-12 不需要实时互联网连接,并且比其在线 Web 基础对应产品提供更好的性能和整体用户体验。另一方面,它需要一次性在特定客户端运行 Microsoft Windows 的计算机上进行产品安装和激活,而基于 Web 的应用程序可以通过任何配备 Web 浏览器的数字平台普遍访问。值得一提的是,桌面应用程序和 Web 应用程序并非相互排斥,因此它们可以在数字教育领域共存,有效互补。

参考文献

  1. 快速整数算法:GCD 和 LCM(CodeProject 文章)
  2. 快速最大公约数 (GCD) 算法
  3. Bing Maps 交互式 SDK
  4. Geographical distance (wiki)
  5. 大圆距离(维基百科)
  6. 中心角(维基百科)
  7. Haversine 公式(维基百科)
  8. Spherical law of cosines (wiki)

历史

  • Edumatter M12-L”精简版 v. 8.1.3 已发布在 Intel 的 AppUp 商店(2012 年 11 月 19 日)
  • Edumatter M-12”标准版 v. 8.1.4 已发布在 Intel 的 AppUp 商店(2012 年 11 月 28 日)
  • FindTutor”应用扩展版本 8.1.1 已发布在 Intel 的 AppUp 商店(2012 年 11 月 26 日)
  • Edumatter-814”标准版 for Windows 7/8,v. 8.1.4.1 于 2014 年 8 月发布
© . All rights reserved.