我需要在 sqlite 数据库中计算欧几里德距离。
除了为数学函数编写和加载动态库之外,有谁知道如何在 sqlite 中计算平方根?
我即将在http://en.wikipedia.org/wiki/Fast_inverse_square_root中使用快速逆平方根算法,尽管它可能会变得比我现在需要的更有趣。
作为旁注,弄清楚如何做功率会很棒(这是一个普遍的问题,并且比将一个数字本身相乘更简洁)。
谢谢,
西蒙妮
我需要在 sqlite 数据库中计算欧几里德距离。
除了为数学函数编写和加载动态库之外,有谁知道如何在 sqlite 中计算平方根?
我即将在http://en.wikipedia.org/wiki/Fast_inverse_square_root中使用快速逆平方根算法,尽管它可能会变得比我现在需要的更有趣。
作为旁注,弄清楚如何做功率会很棒(这是一个普遍的问题,并且比将一个数字本身相乘更简洁)。
谢谢,
西蒙妮
警告:此答案取决于编码语言。在我的情况下C#。
用户定义的 SQLite 函数对我来说实现起来很痛苦。最后,经过长时间的搜索,我能够在我的 C# 代码中实现它。主函数如下所示:
[SQLiteFunction(Arguments = 1, FuncType = FunctionType.Scalar, Name = "Sqrt")]
class Sqrt : SQLiteFunction
{
public override object Invoke(object[] args)
{
return Math.Sqrt(Double.Parse(args[0].ToString()));
}
}
自定义函数的注册:
SQLiteFunction.RegisterFunction(typeof(Sqrt));
并在选择中使用:
SQLiteCommand com = new SQLiteCommand("select sqrt(10.42)", connection);
你可以在这里下载完整的例子:http: //db.tt/qzeNXwso
或者,如果您只想查看代码(或浏览我的代码的所有部分),我将在 SQLite 数据库中计算平方根的完整工作示例代码粘贴在下面,因为很难找到任何工作代码。要创建和运行此示例,请执行以下 6 个步骤:
打开App.config并替换为这个(没有这一步你可能会得到混合模式组装错误):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
用以下代码替换您的Form1.Designer.cs:
namespace Sqrt
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.txb_Input = new System.Windows.Forms.TextBox();
this.txb_Output = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btn_Calcualte = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// txb_Input
//
this.txb_Input.Location = new System.Drawing.Point(131, 12);
this.txb_Input.Name = "txb_Input";
this.txb_Input.Size = new System.Drawing.Size(201, 20);
this.txb_Input.TabIndex = 0;
//
// txb_Output
//
this.txb_Output.BackColor = System.Drawing.Color.WhiteSmoke;
this.txb_Output.Location = new System.Drawing.Point(131, 38);
this.txb_Output.Name = "txb_Output";
this.txb_Output.ReadOnly = true;
this.txb_Output.Size = new System.Drawing.Size(201, 20);
this.txb_Output.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 15);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(31, 13);
this.label1.TabIndex = 1;
this.label1.Text = "Input";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 41);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(39, 13);
this.label2.TabIndex = 1;
this.label2.Text = "Output";
//
// btn_Calcualte
//
this.btn_Calcualte.Location = new System.Drawing.Point(257, 64);
this.btn_Calcualte.Name = "btn_Calcualte";
this.btn_Calcualte.Size = new System.Drawing.Size(75, 23);
this.btn_Calcualte.TabIndex = 2;
this.btn_Calcualte.Text = "Calculate";
this.btn_Calcualte.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(344, 98);
this.Controls.Add(this.btn_Calcualte);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.txb_Output);
this.Controls.Add(this.txb_Input);
this.Name = "Form1";
this.Text = "Root square example";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox txb_Input;
private System.Windows.Forms.TextBox txb_Output;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button btn_Calcualte;
}
}
打开 Form1.cs(代码)并将代码替换为: using System; 使用 System.Data.SQLite;使用 System.Windows.Forms;
namespace Sqrt
{
// definition of custom sqlite function
[SQLiteFunction(Arguments = 1, FuncType = FunctionType.Scalar, Name = "Sqrt")]
class Sqrt : SQLiteFunction
{
public override object Invoke(object[] args)
{
return Math.Sqrt(Double.Parse(args[0].ToString())); // return result of math sqrt function
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.btn_Calcualte.Click += new System.EventHandler(this.btn_Calcualte_Click);
}
private void btn_Calcualte_Click(object sender, EventArgs e)
{
if (txb_Input.Text.Length == 0)
return;
try { SQLiteConnection.CreateFile(AppDomain.CurrentDomain.BaseDirectory + "test.s3db"); }
catch { }
SQLiteConnection con = new SQLiteConnection("Data Source=test.s3db");
SQLiteFunction.RegisterFunction(typeof(Sqrt)); // register custom function
con.Open();
SQLiteCommand com = new SQLiteCommand("select sqrt(" + txb_Input.Text.Replace(',', '.') + ")", con); // select result
string res = com.ExecuteScalar().ToString();
txb_Output.Text = res;
}
}
}
奔跑,尝试和享受。
这是 10000 以下数字的 sqrt 近似值。它可以扩展到任意数字,并且可以根据需要扩展到任意精度。无论如何,这种表格插值在大多数快速实现中都会发生:
case when weight >= 1 and weight<=10 then 1+0.240253073*(weight-1)
when weight>=10 and weight<=100 then 3.16227766+0.075974693*(weight-10)
when weight>=100 and weight<=1000 then 10+0.024025307*(weight-100)
else 31.6227766+0.007597469 *(weight-1000) end
有一个奇怪的事实是,您在这样的 10 次方根插值表中使用的每个因子都是前一个因子的 0.316227766 倍——因此您可以使这个数字适用于任意大的数字,甚至可以将这些值填满一个表使其适用于任何数字。(这会导致这里的一些压缩吗?)
或者这个可爱的 log10 整数,使用 length 函数(插值表在这里可能工作得更好,但我喜欢 log10 和 length() 是相似的,这适用于任何整数 - 不需要插值。
((length(x)+length(x*2)+length(x*3)
+length(x*4)+length(x*5))/5.0)-1.0
一个比我更好的数学头脑可能会想出更好和更密集的近似值。考虑到 c 中的大多数 sqrt 函数无论如何都使用近似值 - 这是一个非常好的解决方案。
这是唯一的本地方式。
添加了内置的 SQL 数学函数()。(需要 -DSQLITE_ENABLE_MATH_FUNCTIONS 编译时选项。)
sqrt(X) 返回 X 的平方根。如果 X 为负数,则返回 NULL。
只有当数学函数不可用时......真的只是在绝望中,因为这不会很快......
-- bisect to find the square root to any tolerance desired
with
input(n) as (select 500), --input
sqrt(lo, hi, guess, n, i) as (
select 1, n, n/2, n, 0 from input
union all
select case when guess*guess < n then guess else lo end,
case when guess*guess < n then hi else guess end,
case when guess*guess < n then (hi+guess)/2.0 else (lo+guess)/2.0 end,
n ,
i +1 from sqrt
where abs(guess*guess - n) > 0.0001), -- tolerance
sqrt_out(x, n) as (select guess, n from sqrt order by sqrt.i desc limit 1)
select * from sqrt_out